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 | /* |
||
4 | * This file is part of Zippy. |
||
5 | * |
||
6 | * (c) Alchemy <[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 | |||
12 | namespace Alchemy\Zippy\Adapter; |
||
13 | |||
14 | use Alchemy\Zippy\Exception\RuntimeException; |
||
15 | use Alchemy\Zippy\Exception\InvalidArgumentException; |
||
16 | use Alchemy\Zippy\Exception\NotSupportedException; |
||
17 | use Alchemy\Zippy\Archive\Member; |
||
18 | use Alchemy\Zippy\Adapter\Resource\ResourceInterface; |
||
19 | use Alchemy\Zippy\Adapter\Resource\ZipArchiveResource; |
||
20 | use Alchemy\Zippy\Adapter\VersionProbe\ZipExtensionVersionProbe; |
||
21 | use Alchemy\Zippy\Archive\Archive; |
||
22 | use Alchemy\Zippy\Resource\ResourceManager; |
||
23 | use Alchemy\Zippy\Resource\Resource; |
||
24 | |||
25 | /** |
||
26 | * ZipExtensionAdapter allows you to create and extract files from archives |
||
27 | * using PHP Zip extension |
||
28 | * |
||
29 | * @see http://www.php.net/manual/en/book.zip.php |
||
30 | */ |
||
31 | class ZipExtensionAdapter extends AbstractAdapter |
||
32 | { |
||
33 | private $errorCodesMapping = array( |
||
34 | \ZipArchive::ER_EXISTS => "File already exists", |
||
35 | \ZipArchive::ER_INCONS => "Zip archive inconsistent", |
||
36 | \ZipArchive::ER_INVAL => "Invalid argument", |
||
37 | \ZipArchive::ER_MEMORY => "Malloc failure", |
||
38 | \ZipArchive::ER_NOENT => "No such file", |
||
39 | \ZipArchive::ER_NOZIP => "Not a zip archive", |
||
40 | \ZipArchive::ER_OPEN => "Can't open file", |
||
41 | \ZipArchive::ER_READ => "Read error", |
||
42 | \ZipArchive::ER_SEEK => "Seek error" |
||
43 | ); |
||
44 | |||
45 | public function __construct(ResourceManager $manager) |
||
46 | { |
||
47 | parent::__construct($manager); |
||
48 | $this->probe = new ZipExtensionVersionProbe(); |
||
49 | } |
||
50 | |||
51 | /** |
||
52 | * @inheritdoc |
||
53 | */ |
||
54 | protected function doListMembers(ResourceInterface $resource) |
||
55 | { |
||
56 | $members = array(); |
||
57 | for ($i = 0; $i < $resource->getResource()->numFiles; $i++) { |
||
58 | $stat = $resource->getResource()->statIndex($i); |
||
59 | $members[] = new Member( |
||
60 | $resource, |
||
61 | $this, |
||
62 | $stat['name'], |
||
63 | $stat['size'], |
||
64 | new \DateTime('@' . $stat['mtime']), |
||
65 | 0 === strlen($resource->getResource()->getFromIndex($i, 1)) |
||
66 | ); |
||
67 | } |
||
68 | |||
69 | return $members; |
||
70 | } |
||
71 | |||
72 | /** |
||
73 | * @inheritdoc |
||
74 | */ |
||
75 | public static function getName() |
||
76 | { |
||
77 | return 'zip-extension'; |
||
78 | } |
||
79 | |||
80 | /** |
||
81 | * @inheritdoc |
||
82 | */ |
||
83 | protected function doExtract(ResourceInterface $resource, $to) |
||
84 | { |
||
85 | return $this->extractMembers($resource, null, $to); |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | * @inheritdoc |
||
90 | */ |
||
91 | protected function doExtractMembers(ResourceInterface $resource, $members, $to, $overwrite = false) |
||
92 | { |
||
93 | if (null === $to) { |
||
94 | // if no destination is given, will extract to zip current folder |
||
95 | $to = dirname(realpath($resource->getResource()->filename)); |
||
96 | } |
||
97 | |||
98 | if (!is_dir($to)) { |
||
99 | $resource->getResource()->close(); |
||
100 | throw new InvalidArgumentException(sprintf("%s is not a directory", $to)); |
||
101 | } |
||
102 | |||
103 | if (!is_writable($to)) { |
||
104 | $resource->getResource()->close(); |
||
105 | throw new InvalidArgumentException(sprintf("%s is not writable", $to)); |
||
106 | } |
||
107 | |||
108 | if (null !== $members) { |
||
109 | $membersTemp = (array) $members; |
||
110 | if (empty($membersTemp)) { |
||
111 | $resource->getResource()->close(); |
||
112 | |||
113 | throw new InvalidArgumentException("no members provided"); |
||
114 | } |
||
115 | $members = array(); |
||
116 | // allows $members to be an array of strings or array of Members |
||
117 | foreach ($membersTemp as $member) { |
||
118 | if ($member instanceof Member) { |
||
119 | $member = $member->getLocation(); |
||
120 | } |
||
121 | |||
122 | if ($resource->getResource()->locateName($member) === false) { |
||
123 | $resource->getResource()->close(); |
||
124 | |||
125 | throw new InvalidArgumentException(sprintf('%s is not in the zip file', $member)); |
||
126 | } |
||
127 | |||
128 | if ($overwrite == false) { |
||
0 ignored issues
–
show
|
|||
129 | if (file_exists($member)) { |
||
130 | $resource->getResource()->close(); |
||
131 | |||
132 | throw new RuntimeException('Target file ' . $member . ' already exists.'); |
||
133 | } |
||
134 | } |
||
135 | |||
136 | $members[] = $member; |
||
137 | } |
||
138 | } |
||
139 | |||
140 | if (!$resource->getResource()->extractTo($to, $members)) { |
||
141 | $resource->getResource()->close(); |
||
142 | |||
143 | throw new InvalidArgumentException(sprintf('Unable to extract archive : %s', $resource->getResource()->getStatusString())); |
||
144 | } |
||
145 | |||
146 | return new \SplFileInfo($to); |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * @inheritdoc |
||
151 | */ |
||
152 | protected function doRemove(ResourceInterface $resource, $files) |
||
153 | { |
||
154 | $files = (array) $files; |
||
155 | |||
156 | if (empty($files)) { |
||
157 | throw new InvalidArgumentException("no files provided"); |
||
158 | } |
||
159 | |||
160 | // either remove all files or none in case of error |
||
161 | foreach ($files as $file) { |
||
162 | if ($resource->getResource()->locateName($file) === false) { |
||
163 | $resource->getResource()->unchangeAll(); |
||
164 | $resource->getResource()->close(); |
||
165 | |||
166 | throw new InvalidArgumentException(sprintf('%s is not in the zip file', $file)); |
||
167 | } |
||
168 | if (!$resource->getResource()->deleteName($file)) { |
||
169 | $resource->getResource()->unchangeAll(); |
||
170 | $resource->getResource()->close(); |
||
171 | |||
172 | throw new RuntimeException(sprintf('unable to remove %s', $file)); |
||
173 | } |
||
174 | } |
||
175 | $this->flush($resource->getResource()); |
||
176 | |||
177 | return $files; |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * @inheritdoc |
||
182 | */ |
||
183 | protected function doAdd(ResourceInterface $resource, $files, $recursive) |
||
184 | { |
||
185 | $files = (array) $files; |
||
186 | if (empty($files)) { |
||
187 | $resource->getResource()->close(); |
||
188 | throw new InvalidArgumentException("no files provided"); |
||
189 | } |
||
190 | $this->addEntries($resource, $files, $recursive); |
||
0 ignored issues
–
show
$resource of type object<Alchemy\Zippy\Ada...urce\ResourceInterface> is not a sub-type of object<Alchemy\Zippy\Ada...rce\ZipArchiveResource> . It seems like you assume a concrete implementation of the interface Alchemy\Zippy\Adapter\Resource\ResourceInterface to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
191 | |||
192 | return $files; |
||
193 | } |
||
194 | |||
195 | /** |
||
196 | * @inheritdoc |
||
197 | */ |
||
198 | protected function doCreate($path, $files, $recursive) |
||
199 | { |
||
200 | $files = (array) $files; |
||
201 | |||
202 | if (empty($files)) { |
||
203 | throw new NotSupportedException("Cannot create an empty zip"); |
||
204 | } |
||
205 | |||
206 | $resource = $this->getResource($path, \ZipArchive::CREATE); |
||
207 | $this->addEntries($resource, $files, $recursive); |
||
208 | |||
209 | return new Archive($resource, $this, $this->manager); |
||
210 | } |
||
211 | |||
212 | /** |
||
213 | * Returns a new instance of the invoked adapter |
||
214 | * |
||
215 | * @return AbstractAdapter |
||
216 | * |
||
217 | * @throws RuntimeException In case object could not be instanciated |
||
218 | */ |
||
219 | public static function newInstance() |
||
220 | { |
||
221 | return new ZipExtensionAdapter(ResourceManager::create()); |
||
222 | } |
||
223 | |||
224 | protected function createResource($path) |
||
225 | { |
||
226 | return $this->getResource($path, \ZipArchive::CHECKCONS); |
||
227 | } |
||
228 | |||
229 | private function getResource($path, $mode) |
||
230 | { |
||
231 | $zip = new \ZipArchive(); |
||
232 | $res = $zip->open($path, $mode); |
||
233 | |||
234 | if ($res !== true) { |
||
235 | throw new RuntimeException($this->errorCodesMapping[$res]); |
||
236 | } |
||
237 | |||
238 | return new ZipArchiveResource($zip); |
||
239 | } |
||
240 | |||
241 | private function addEntries(ZipArchiveResource $zipresource, array $files, $recursive) |
||
242 | { |
||
243 | $stack = new \SplStack(); |
||
244 | |||
245 | $error = null; |
||
246 | $cwd = getcwd(); |
||
247 | $collection = $this->manager->handle($cwd, $files); |
||
248 | |||
249 | $this->chdir($collection->getContext()); |
||
250 | |||
251 | $adapter = $this; |
||
252 | |||
253 | try { |
||
254 | $collection->forAll(function($i, Resource $resource) use ($zipresource, $stack, $recursive, $adapter) { |
||
255 | $adapter->checkReadability($zipresource->getResource(), $resource->getTarget()); |
||
256 | if (is_dir($resource->getTarget())) { |
||
257 | if ($recursive) { |
||
258 | $stack->push($resource->getTarget() . ((substr($resource->getTarget(), -1) === DIRECTORY_SEPARATOR) ? '' : DIRECTORY_SEPARATOR)); |
||
259 | } else { |
||
260 | $adapter->addEmptyDir($zipresource->getResource(), $resource->getTarget()); |
||
261 | } |
||
262 | } else { |
||
263 | $adapter->addFileToZip($zipresource->getResource(), $resource->getTarget()); |
||
264 | } |
||
265 | |||
266 | return true; |
||
267 | }); |
||
268 | |||
269 | // recursively add dirs |
||
270 | while (!$stack->isEmpty()) { |
||
271 | $dir = $stack->pop(); |
||
272 | // removes . and .. |
||
273 | $files = array_diff(scandir($dir), array(".", "..")); |
||
274 | if (count($files) > 0) { |
||
275 | foreach ($files as $file) { |
||
276 | $file = $dir . $file; |
||
277 | $this->checkReadability($zipresource->getResource(), $file); |
||
278 | if (is_dir($file)) { |
||
279 | $stack->push($file . DIRECTORY_SEPARATOR); |
||
280 | } else { |
||
281 | $this->addFileToZip($zipresource->getResource(), $file); |
||
282 | } |
||
283 | } |
||
284 | } else { |
||
285 | $this->addEmptyDir($zipresource->getResource(), $dir); |
||
286 | } |
||
287 | } |
||
288 | $this->flush($zipresource->getResource()); |
||
289 | |||
290 | $this->manager->cleanup($collection); |
||
291 | } catch (\Exception $e) { |
||
292 | $error = $e; |
||
293 | } |
||
294 | |||
295 | $this->chdir($cwd); |
||
296 | |||
297 | if ($error) { |
||
298 | throw $error; |
||
299 | } |
||
300 | } |
||
301 | |||
302 | /** |
||
303 | * @info is public for PHP 5.3 compatibility, should be private |
||
304 | * @param string $file |
||
305 | */ |
||
306 | public function checkReadability(\ZipArchive $zip, $file) |
||
307 | { |
||
308 | if (!is_readable($file)) { |
||
309 | $zip->unchangeAll(); |
||
310 | $zip->close(); |
||
311 | |||
312 | throw new InvalidArgumentException(sprintf('could not read %s', $file)); |
||
313 | } |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * @info is public for PHP 5.3 compatibility, should be private |
||
318 | * @param string $file |
||
319 | */ |
||
320 | View Code Duplication | public function addFileToZip(\ZipArchive $zip, $file) |
|
0 ignored issues
–
show
This method seems to be duplicated in 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. ![]() |
|||
321 | { |
||
322 | if (!$zip->addFile($file)) { |
||
323 | $zip->unchangeAll(); |
||
324 | $zip->close(); |
||
325 | |||
326 | throw new RuntimeException(sprintf('unable to add %s to the zip file', $file)); |
||
327 | } |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * @info is public for PHP 5.3 compatibility, should be private |
||
332 | */ |
||
333 | View Code Duplication | public function addEmptyDir(\ZipArchive $zip, $dir) |
|
0 ignored issues
–
show
This method seems to be duplicated in 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. ![]() |
|||
334 | { |
||
335 | if (!$zip->addEmptyDir($dir)) { |
||
336 | $zip->unchangeAll(); |
||
337 | $zip->close(); |
||
338 | |||
339 | throw new RuntimeException(sprintf('unable to add %s to the zip file', $dir)); |
||
340 | } |
||
341 | } |
||
342 | |||
343 | /** |
||
344 | * Flushes changes to the archive |
||
345 | * |
||
346 | * @param \ZipArchive $zip |
||
347 | */ |
||
348 | private function flush(\ZipArchive $zip) // flush changes by reopening the file |
||
349 | { |
||
350 | $path = $zip->filename; |
||
351 | $zip->close(); |
||
352 | $zip->open($path, \ZipArchive::CHECKCONS); |
||
353 | } |
||
354 | } |
||
355 |
When comparing two booleans, it is generally considered safer to use the strict comparison operator.