Issues (11)

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

Labels

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
 * Upload Manager.
4
 *
5
 * This file contains upload manager.
6
 *
7
 * @author  Martin Stolz <[email protected]>
8
 */
9
10
namespace herroffizier\yii2um;
11
12
use Yii;
13
use yii\base\Component;
14
use yii\base\InvalidParamException;
15
use yii\web\UploadedFile;
16
use yii\helpers\FileHelper;
17
18
class UploadManager extends Component
19
{
20
    /**
21
     * Throw exception when trying to overwrite existing file.
22
     */
23
    const STRATEGY_KEEP = 0;
24
25
    /**
26
     * Overwrite existing file silently.
27
     */
28
    const STRATEGY_OVERWRITE = 1;
29
30
    /**
31
     * Rename new file if file with same name exists.
32
     */
33
    const STRATEGY_RENAME = 2;
34
35
    /**
36
     * Path to upload folder.
37
     *
38
     * @var string
39
     */
40
    public $uploadDir = '@webroot/upload';
41
42
    /**
43
     * URL to upload folder.
44
     *
45
     * @var string
46
     */
47
    public $uploadUrl = '@web/upload';
48
49
    /**
50
     * Generate partition name based on file name.
51
     *
52
     * @param string $name
53
     *
54
     * @return string
55
     */
56 20
    protected function getPartitionName($name)
57
    {
58 20
        return substr(md5(mb_substr($name, 0, 2)), 0, 2);
59
    }
60
61
    /**
62
     * Add partition folder to given path.
63
     *
64
     * @param string $path
65
     * @param string $name
66
     *
67
     * @return string
68
     */
69 20
    protected function getPartitionedPath($path, $name)
70
    {
71 20
        $subfolder = $this->getPartitionName($name);
72 20
        $path = FileHelper::normalizePath($path).'/'.$subfolder;
73
74 20
        return $path;
75
    }
76
77
    /**
78
     * Get prefixed path.
79
     *
80
     * @param string $path
81
     * @param string $prefix
82
     *
83
     * @return string
84
     */
85 36
    protected function getPrefixedPath($path, $prefix)
86
    {
87 36
        if (substr($path, 0, 1) !== '/') {
88 36
            $path = '/'.$path;
89 27
        }
90 36
        $path = FileHelper::normalizePath($path);
91 36
        $prefixedPath = Yii::getAlias($prefix.$path);
92
93 36
        return $prefixedPath;
94
    }
95
96
    /**
97
     * Add index to file name.
98
     *
99
     * @param string $name
100
     * @param int    $index
101
     *
102
     * @return string
103
     */
104 4
    protected function addIndexToName($name, $index)
105
    {
106 4
        $pathinfo = pathinfo($name);
107
108 4
        if (empty($pathinfo['extension'])) {
109 4
            return $pathinfo['basename'].'-'.$index;
110
        } else {
111 4
            return $pathinfo['filename'].'-'.$index.'.'.$pathinfo['extension'];
112
        }
113
    }
114
115
    /**
116
     * Pick up file name according to overwrite strategy and create path.
117
     *
118
     * @throws InvalidParamException when file cannot be created.
119
     *
120
     * @param string $path
121
     * @param string $name
122
     * @param int    $overwriteStrategy
123
     *
124
     * @return string
125
     */
126 16
    protected function createFilePath($path, $name, $overwriteStrategy)
127
    {
128 16
        $partitionedPath = $this->getPartitionedPath($path, $name);
129 16
        $absolutePath = $this->getAbsolutePath($partitionedPath);
130
131 16
        if (file_exists($absolutePath.'/'.$name)) {
132
            switch ($overwriteStrategy) {
133 4
                case self::STRATEGY_KEEP:
134
                    // File overwrtiting is forbidden.
135 4
                    throw new InvalidParamException('File '.$name.' already exists in '.$path.'.');
136
137 4
                case self::STRATEGY_RENAME:
138 4
                    $index = 0;
139
                    do {
140 4
                        ++$index;
141 4
                        $indexedName = $this->addIndexToName($name, $index);
142
143 4
                        $partitionedPath = $this->getPartitionedPath($path, $name);
144 4
                        $absolutePath = $this->getAbsolutePath($partitionedPath);
145 4
                    } while (file_exists($absolutePath.'/'.$indexedName));
146 4
                    $name = $indexedName;
147 4
                    break;
148
149 4
                case self::STRATEGY_OVERWRITE:
150 4
                    if (is_dir($absolutePath.'/'.$name)) {
151
                        // Cannot overwrtite folder.
152 4
                        throw new InvalidParamException($path.'/'.$name.' is a directory and cannot be overwritten.');
153
                    }
154 4
                    break;
155
            }
156 3
        }
157
158 16
        $this->createPath($partitionedPath);
159
160 16
        return $partitionedPath.'/'.$name;
161
    }
162
163
    /**
164
     * Get relative URL for relative path.
165
     *
166
     * @param string $path
167
     *
168
     * @return string
169
     */
170 4
    public function getUrl($path)
171
    {
172 4
        return $this->getPrefixedPath($path, $this->uploadUrl);
173
    }
174
175
    /**
176
     * Get absolute path for relative path.
177
     *
178
     * @param string $path
179
     *
180
     * @return string
181
     */
182 32
    public function getAbsolutePath($path)
183
    {
184 32
        return $this->getPrefixedPath($path, $this->uploadDir);
185
    }
186
187
    /**
188
     * Create given folder tree in upload folder.
189
     *
190
     * Returns absolute path of given path.
191
     *
192
     * @throws InvalidParamException when file cannot be created.
193
     *
194
     * @param string $path
195
     *
196
     * @return string
197
     */
198 24
    public function createPath($path)
199
    {
200 24
        $absolutePath = $this->getAbsolutePath($path);
201 24
        if (!file_exists($absolutePath)) {
202
            // FIXME Check return value.
203 24
            FileHelper::createDirectory($absolutePath);
0 ignored issues
show
It seems like $absolutePath defined by $this->getAbsolutePath($path) on line 200 can also be of type boolean; however, yii\helpers\BaseFileHelper::createDirectory() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
204 22
        } elseif (!is_dir($absolutePath)) {
205 4
            throw new InvalidParamException($path.' is a file, cannot create folder with the same name.');
206
        }
207
208 24
        return $absolutePath;
209
    }
210
211
    /**
212
     * Create folder tree appended with partition folder in upload folder.
213
     *
214
     * Partition folder name depends on given file name.
215
     *
216
     * Returns absolute path of given path.
217
     *
218
     * @param string $path
219
     * @param string $name
220
     *
221
     * @return string
222
     */
223 4
    public function createPartitionedPath($path, $name)
224
    {
225 4
        $path = $this->getPartitionedPath($path, $name);
226
227 4
        return $this->createPath($path);
228
    }
229
230
    /**
231
     * Whether file with given relative path exists.
232
     *
233
     * @param string $filePath
234
     *
235
     * @return bool
236
     */
237 4
    public function exists($filePath)
238
    {
239 4
        $absoluteFilePath = $this->getAbsolutePath($filePath);
240
241 4
        return file_exists($absoluteFilePath);
242
    }
243
244
    /**
245
     * Save data stored in $content as file to $path/$name in upload folder.
246
     *
247
     * Returns relative path with partition folder.
248
     *
249
     * @param string        $path
250
     * @param string        $name
251
     * @param string        $content
252
     * @param int[optional] $overwriteStrategy
253
     *
254
     * @return string
255
     */
256 4 View Code Duplication
    public function saveContent($path, $name, $content, $overwriteStrategy = self::STRATEGY_KEEP)
257
    {
258 4
        $filePath = $this->createFilePath($path, $name, $overwriteStrategy);
259 4
        $absoluteFilePath = $this->getAbsolutePath($filePath);
260
261 4
        file_put_contents($absoluteFilePath, $content);
262 4
        unset($content);
263
264 4
        return $filePath;
265
    }
266
267
    /**
268
     * Save $upload file to $path in upload folder.
269
     *
270
     * Returns relative path with partition folder.
271
     *
272
     * @param string        $path
273
     * @param UploadedFile  $upload
274
     * @param int[optional] $overwriteStrategy
275
     *
276
     * @return string
277
     */
278 4 View Code Duplication
    public function saveUpload($path, UploadedFile $upload, $overwriteStrategy = self::STRATEGY_KEEP)
279
    {
280 4
        $filePath = $this->createFilePath($path, $upload->name, $overwriteStrategy);
281 4
        $absoluteFilePath = $this->getAbsolutePath($filePath);
282
283 4
        $upload->saveAs($absoluteFilePath);
0 ignored issues
show
It seems like $absoluteFilePath defined by $this->getAbsolutePath($filePath) on line 281 can also be of type boolean; however, yii\web\UploadedFile::saveAs() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
284
285 4
        return $filePath;
286
    }
287
288
    /**
289
     * Move or copy file.
290
     *
291
     * @param string        $path
292
     * @param string        $absoluteFilePath
293
     * @param int[optional] $overwriteStrategy
294
     * @param string        $function
295
     *
296
     * @return string
297
     */
298 8 View Code Duplication
    protected function saveFileInternal($path, $absoluteFilePath, $overwriteStrategy, $function)
299
    {
300 8
        $filePath = $this->createFilePath($path, pathinfo($absoluteFilePath, PATHINFO_BASENAME), $overwriteStrategy);
301 8
        $newAbsoluteFilePath = $this->getAbsolutePath($filePath);
302
303 8
        $function($absoluteFilePath, $newAbsoluteFilePath);
304
305 8
        return $filePath;
306
    }
307
308
    /**
309
     * Save $absoluteFilePath file to $path in upload folder.
310
     *
311
     * Returns relative path with partition folder.
312
     *
313
     * @param string        $path
314
     * @param string        $absoluteFilePath
315
     * @param int[optional] $overwriteStrategy
316
     *
317
     * @return string
318
     */
319 8
    public function saveFile($path, $absoluteFilePath, $overwriteStrategy = self::STRATEGY_KEEP)
320
    {
321 8
        return $this->saveFileInternal($path, $absoluteFilePath, $overwriteStrategy, 'copy');
322
    }
323
324
    /**
325
     * Move $absoluteFilePath file to $path in upload folder.
326
     *
327
     * Returns relative path with partition folder.
328
     *
329
     * @param string        $path
330
     * @param string        $absoluteFilePath
331
     * @param int[optional] $overwriteStrategy
332
     *
333
     * @return string
334
     */
335 4
    public function moveFile($path, $absoluteFilePath, $overwriteStrategy = self::STRATEGY_KEEP)
336
    {
337 4
        return $this->saveFileInternal($path, $absoluteFilePath, $overwriteStrategy, 'rename');
338
    }
339
}
340