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 | * This file is part of Composer. |
||
4 | * |
||
5 | * (c) Nils Adermann <[email protected]> |
||
6 | * Jordi Boggiano <[email protected]> |
||
7 | * |
||
8 | * For the full copyright and license information, please view the LICENSE |
||
9 | * file that was distributed with this source code. |
||
10 | * |
||
11 | * PHP Version 5 |
||
12 | * |
||
13 | * @category Netresearch |
||
14 | * @package Netresearch\Kite |
||
15 | * @subpackage Service |
||
16 | * @author Christian Opitz <[email protected]> |
||
17 | * @license https://github.com/composer/composer/blob/master/LICENSE Composer license |
||
18 | * @link http://www.netresearch.de |
||
19 | */ |
||
20 | |||
21 | namespace Netresearch\Kite\Service; |
||
22 | |||
23 | use RecursiveDirectoryIterator; |
||
24 | use RecursiveIteratorIterator; |
||
25 | |||
26 | /** |
||
27 | * A shell command service |
||
28 | * |
||
29 | * @category Netresearch |
||
30 | * @package Netresearch\Kite |
||
31 | * @subpackage Service |
||
32 | * @author Jordi Boggiano <[email protected]> |
||
33 | * @author Johannes M. Schmitt <[email protected]> |
||
34 | * @author Christian Opitz <[email protected]> |
||
35 | * @license https://github.com/composer/composer/blob/master/LICENSE Composer license |
||
36 | * @link http://www.netresearch.de |
||
37 | */ |
||
38 | class Filesystem |
||
39 | { |
||
40 | /** |
||
41 | * @var Console |
||
42 | */ |
||
43 | protected $console; |
||
44 | |||
45 | /** |
||
46 | * Construct Filesystem |
||
47 | * |
||
48 | * @param Console $console The console |
||
49 | */ |
||
50 | public function __construct(Console $console) |
||
51 | { |
||
52 | $this->console = $console; |
||
53 | } |
||
54 | |||
55 | |||
56 | /** |
||
57 | * Remove a file |
||
58 | * |
||
59 | * @param string $file The file or dir |
||
60 | * |
||
61 | * @return bool |
||
62 | */ |
||
63 | public function remove($file) |
||
64 | { |
||
65 | if (is_dir($file)) { |
||
66 | return $this->removeDirectory($file); |
||
67 | } |
||
68 | |||
69 | if (file_exists($file)) { |
||
70 | return unlink($file); |
||
71 | } |
||
72 | |||
73 | return false; |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * Checks if a directory is empty |
||
78 | * |
||
79 | * @param string $dir The dir |
||
80 | * |
||
81 | * @return bool |
||
82 | */ |
||
83 | public function isDirEmpty($dir) |
||
84 | { |
||
85 | $dir = rtrim($dir, '/\\'); |
||
86 | |||
87 | return count(glob($dir . '/*') ?: array()) === 0 && count(glob($dir . '/.*') ?: array()) === 2; |
||
88 | } |
||
89 | |||
90 | /** |
||
91 | * Recursively remove a directory |
||
92 | * |
||
93 | * Uses the process component if proc_open is enabled on the PHP |
||
94 | * installation. |
||
95 | * |
||
96 | * @param string $directory The dir |
||
97 | * |
||
98 | * @return bool |
||
99 | */ |
||
100 | public function removeDirectory($directory) |
||
101 | { |
||
102 | if (!is_dir($directory)) { |
||
103 | return true; |
||
104 | } |
||
105 | |||
106 | if (preg_match('{^(?:[a-z]:)?[/\\\\]+$}i', $directory)) { |
||
107 | throw new \RuntimeException('Aborting an attempted deletion of ' . $directory . ', this was probably not intended, if it is a real use case please report it.'); |
||
108 | } |
||
109 | |||
110 | if (!function_exists('proc_open')) { |
||
111 | return $this->removeDirectoryPhp($directory); |
||
112 | } |
||
113 | |||
114 | if (defined('PHP_WINDOWS_VERSION_BUILD')) { |
||
115 | $cmd = sprintf('rmdir /S /Q %s', escapeshellarg(realpath($directory))); |
||
116 | } else { |
||
117 | $cmd = sprintf('rm -rf %s', escapeshellarg($directory)); |
||
118 | } |
||
119 | |||
120 | $this->console->createProcess($cmd)->run(); |
||
121 | |||
122 | return !is_dir($directory); |
||
123 | } |
||
124 | |||
125 | /** |
||
126 | * Recursively delete directory using PHP iterators. |
||
127 | * |
||
128 | * Uses a CHILD_FIRST RecursiveIteratorIterator to sort files |
||
129 | * before directories, creating a single non-recursive loop |
||
130 | * to delete files/directories in the correct order. |
||
131 | * |
||
132 | * @param string $directory The dir |
||
133 | * |
||
134 | * @return bool |
||
135 | */ |
||
136 | public function removeDirectoryPhp($directory) |
||
137 | { |
||
138 | $it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS); |
||
139 | $ri = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST); |
||
140 | |||
141 | foreach ($ri as $file) { |
||
142 | if ($file->isDir()) { |
||
143 | rmdir($file->getPathname()); |
||
144 | } else { |
||
145 | unlink($file->getPathname()); |
||
146 | } |
||
147 | } |
||
148 | |||
149 | return rmdir($directory); |
||
150 | } |
||
151 | |||
152 | /** |
||
153 | * Ensures a dir exists |
||
154 | * |
||
155 | * @param string $directory The dir |
||
156 | * |
||
157 | * @return void |
||
158 | */ |
||
159 | public function ensureDirectoryExists($directory) |
||
160 | { |
||
161 | if (!is_dir($directory)) { |
||
162 | if (file_exists($directory)) { |
||
163 | throw new \RuntimeException( |
||
164 | $directory . ' exists and is not a directory.' |
||
165 | ); |
||
166 | } |
||
167 | if (!@mkdir($directory, 0777, true)) { |
||
168 | throw new \RuntimeException( |
||
169 | $directory . ' does not exist and could not be created.' |
||
170 | ); |
||
171 | } |
||
172 | } |
||
173 | } |
||
174 | |||
175 | /** |
||
176 | * Copy then delete is a non-atomic version of {@link rename}. |
||
177 | * |
||
178 | * Some systems can't rename and also don't have proc_open, |
||
179 | * which requires this solution. |
||
180 | * |
||
181 | * @param string $source The source |
||
182 | * @param string $target The target |
||
183 | * |
||
184 | * @return void |
||
185 | */ |
||
186 | public function copyThenRemove($source, $target) |
||
187 | { |
||
188 | $this->copy($source, $target); |
||
189 | $this->removeDirectoryPhp($source); |
||
190 | } |
||
191 | |||
192 | /** |
||
193 | * Copy a file or folder to a target destination |
||
194 | * |
||
195 | * @param string $source The source |
||
196 | * @param string $target The target |
||
197 | * |
||
198 | * @return void |
||
199 | */ |
||
200 | public function copy($source, $target) |
||
201 | { |
||
202 | $path = realpath($source); |
||
203 | if (is_dir($path)) { |
||
204 | $it = new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS); |
||
205 | $ri = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::SELF_FIRST); |
||
206 | |||
207 | $this->ensureDirectoryExists($target); |
||
208 | |||
209 | foreach ($ri as $file) { |
||
210 | $targetPath = $target . DIRECTORY_SEPARATOR . $ri->getSubPathName(); |
||
211 | if ($file->isDir()) { |
||
212 | $this->ensureDirectoryExists($targetPath); |
||
213 | } else { |
||
214 | copy($file->getPathname(), $targetPath); |
||
215 | } |
||
216 | } |
||
217 | } else { |
||
218 | $this->ensureDirectoryExists(dirname($target)); |
||
219 | copy($source, $target); |
||
220 | } |
||
221 | } |
||
222 | |||
223 | /** |
||
224 | * Rename a file or folder to a target destination |
||
225 | * |
||
226 | * @param string $source The source |
||
227 | * @param string $target The target |
||
228 | * |
||
229 | * @return void |
||
230 | */ |
||
231 | public function rename($source, $target) |
||
232 | { |
||
233 | if (true === @rename($source, $target)) { |
||
234 | return; |
||
235 | } |
||
236 | |||
237 | if (!function_exists('proc_open')) { |
||
238 | return $this->copyThenRemove($source, $target); |
||
239 | } |
||
240 | |||
241 | if (defined('PHP_WINDOWS_VERSION_BUILD')) { |
||
242 | // Try to copy & delete - this is a workaround for random "Access denied" errors. |
||
243 | $command = sprintf('xcopy %s %s /E /I /Q', escapeshellarg($source), escapeshellarg($target)); |
||
244 | $result = $this->console->createProcess($command)->run(); |
||
245 | |||
246 | // clear stat cache because external processes aren't tracked by the php stat cache |
||
247 | clearstatcache(); |
||
248 | |||
249 | if (0 === $result) { |
||
250 | $this->remove($source); |
||
251 | |||
252 | return; |
||
253 | } |
||
254 | } else { |
||
255 | // We do not use PHP's "rename" function here since it does not support |
||
256 | // the case where $source, and $target are located on different partitions. |
||
257 | $command = sprintf('mv %s %s', escapeshellarg($source), escapeshellarg($target)); |
||
258 | try { |
||
259 | $this->console->createProcess($command)->run(); |
||
260 | // clear stat cache because external processes aren't tracked by the php stat cache |
||
261 | clearstatcache(); |
||
262 | return; |
||
263 | } catch (\Exception $e) { |
||
264 | // do copyThenRemove |
||
265 | } |
||
266 | } |
||
267 | |||
268 | return $this->copyThenRemove($source, $target); |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * Returns the shortest path from $from to $to |
||
273 | * |
||
274 | * @param string $from From |
||
275 | * @param string $to To |
||
276 | * @param bool $directories if true, the source/target are considered to be directories |
||
277 | * |
||
278 | * @throws \InvalidArgumentException |
||
279 | * |
||
280 | * @return string |
||
281 | */ |
||
282 | public function findShortestPath($from, $to, $directories = false) |
||
283 | { |
||
284 | View Code Duplication | if (!$this->isAbsolutePath($from) || !$this->isAbsolutePath($to)) { |
|
0 ignored issues
–
show
|
|||
285 | throw new \InvalidArgumentException(sprintf('$from (%s) and $to (%s) must be absolute paths.', $from, $to)); |
||
286 | } |
||
287 | |||
288 | $from = lcfirst($this->normalizePath($from)); |
||
289 | $to = lcfirst($this->normalizePath($to)); |
||
290 | |||
291 | if ($directories) { |
||
292 | $from .= '/dummy_file'; |
||
293 | } |
||
294 | |||
295 | if (dirname($from) === dirname($to)) { |
||
296 | return './' . basename($to); |
||
297 | } |
||
298 | |||
299 | $commonPath = $to; |
||
300 | View Code Duplication | while (strpos($from . '/', $commonPath . '/') !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
301 | $commonPath = strtr(dirname($commonPath), '\\', '/'); |
||
302 | } |
||
303 | |||
304 | if (0 !== strpos($from, $commonPath) || '/' === $commonPath) { |
||
305 | return $to; |
||
306 | } |
||
307 | |||
308 | $commonPath = rtrim($commonPath, '/') . '/'; |
||
309 | $sourcePathDepth = substr_count(substr($from, strlen($commonPath)), '/'); |
||
310 | $commonPathCode = str_repeat('../', $sourcePathDepth); |
||
311 | |||
312 | return ($commonPathCode . substr($to, strlen($commonPath))) ?: './'; |
||
313 | } |
||
314 | |||
315 | /** |
||
316 | * Returns PHP code that, when executed in $from, will return the path to $to |
||
317 | * |
||
318 | * @param string $from From |
||
319 | * @param string $to To |
||
320 | * @param bool $directories if true, the source/target are considered to be directories |
||
321 | * |
||
322 | * @throws \InvalidArgumentException |
||
323 | * |
||
324 | * @return string |
||
325 | */ |
||
326 | public function findShortestPathCode($from, $to, $directories = false) |
||
327 | { |
||
328 | View Code Duplication | if (!$this->isAbsolutePath($from) || !$this->isAbsolutePath($to)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
329 | throw new \InvalidArgumentException(sprintf('$from (%s) and $to (%s) must be absolute paths.', $from, $to)); |
||
330 | } |
||
331 | |||
332 | $from = lcfirst($this->normalizePath($from)); |
||
333 | $to = lcfirst($this->normalizePath($to)); |
||
334 | |||
335 | if ($from === $to) { |
||
336 | return $directories ? '__DIR__' : '__FILE__'; |
||
337 | } |
||
338 | |||
339 | $commonPath = $to; |
||
340 | View Code Duplication | while (strpos($from . '/', $commonPath . '/') !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath) && '.' !== $commonPath) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
341 | $commonPath = strtr(dirname($commonPath), '\\', '/'); |
||
342 | } |
||
343 | |||
344 | if (0 !== strpos($from, $commonPath) || '/' === $commonPath || '.' === $commonPath) { |
||
345 | return var_export($to, true); |
||
346 | } |
||
347 | |||
348 | $commonPath = rtrim($commonPath, '/') . '/'; |
||
349 | if (strpos($to, $from . '/') === 0) { |
||
350 | return '__DIR__ . ' . var_export(substr($to, strlen($from)), true); |
||
351 | } |
||
352 | $sourcePathDepth = substr_count(substr($from, strlen($commonPath)), '/') + $directories; |
||
353 | $commonPathCode = str_repeat('dirname(', $sourcePathDepth) . '__DIR__' . str_repeat(')', $sourcePathDepth); |
||
354 | $relTarget = substr($to, strlen($commonPath)); |
||
355 | |||
356 | return $commonPathCode . (strlen($relTarget) ? '.' . var_export('/' . $relTarget, true) : ''); |
||
357 | } |
||
358 | |||
359 | /** |
||
360 | * Checks if the given path is absolute |
||
361 | * |
||
362 | * @param string $path Path |
||
363 | * |
||
364 | * @return bool |
||
365 | */ |
||
366 | public function isAbsolutePath($path) |
||
367 | { |
||
368 | return substr($path, 0, 1) === '/' || substr($path, 1, 1) === ':'; |
||
369 | } |
||
370 | |||
371 | /** |
||
372 | * Returns size of a file or directory specified by path. If a directory is |
||
373 | * given, it's size will be computed recursively. |
||
374 | * |
||
375 | * @param string $path Path to the file or directory |
||
376 | * |
||
377 | * @throws \RuntimeException |
||
378 | * |
||
379 | * @return int |
||
380 | */ |
||
381 | public function size($path) |
||
382 | { |
||
383 | if (!file_exists($path)) { |
||
384 | throw new \RuntimeException("$path does not exist."); |
||
385 | } |
||
386 | if (is_dir($path)) { |
||
387 | return $this->directorySize($path); |
||
388 | } |
||
389 | |||
390 | return filesize($path); |
||
391 | } |
||
392 | |||
393 | /** |
||
394 | * Normalize a path. This replaces backslashes with slashes, removes ending |
||
395 | * slash and collapses redundant separators and up-level references. |
||
396 | * |
||
397 | * @param string $path Path to the file or directory |
||
398 | * |
||
399 | * @return string |
||
400 | */ |
||
401 | public function normalizePath($path) |
||
402 | { |
||
403 | $parts = array(); |
||
404 | $path = strtr($path, '\\', '/'); |
||
405 | $prefix = ''; |
||
406 | $absolute = false; |
||
407 | |||
408 | if (preg_match('{^([0-9a-z]+:(?://(?:[a-z]:)?)?)}i', $path, $match)) { |
||
409 | $prefix = $match[1]; |
||
410 | $path = substr($path, strlen($prefix)); |
||
411 | } |
||
412 | |||
413 | if (substr($path, 0, 1) === '/') { |
||
414 | $absolute = true; |
||
415 | $path = substr($path, 1); |
||
416 | } |
||
417 | |||
418 | $up = false; |
||
419 | foreach (explode('/', $path) as $chunk) { |
||
420 | if ('..' === $chunk && ($absolute || $up)) { |
||
421 | array_pop($parts); |
||
422 | $up = !(empty($parts) || '..' === end($parts)); |
||
423 | } elseif ('.' !== $chunk && '' !== $chunk) { |
||
424 | $parts[] = $chunk; |
||
425 | $up = '..' !== $chunk; |
||
426 | } |
||
427 | } |
||
428 | |||
429 | return $prefix . ($absolute ? '/' : '') . implode('/', $parts); |
||
430 | } |
||
431 | |||
432 | /** |
||
433 | * Get directory size |
||
434 | * |
||
435 | * @param string $directory The dir |
||
436 | * |
||
437 | * @return int |
||
438 | */ |
||
439 | protected function directorySize($directory) |
||
440 | { |
||
441 | $it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS); |
||
442 | $ri = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST); |
||
443 | |||
444 | $size = 0; |
||
445 | foreach ($ri as $file) { |
||
446 | if ($file->isFile()) { |
||
447 | $size += $file->getSize(); |
||
448 | } |
||
449 | } |
||
450 | |||
451 | return $size; |
||
452 | } |
||
453 | } |
||
454 | ?> |
||
455 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.