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 EC-CUBE |
||
5 | * |
||
6 | * Copyright(c) 2000-2015 LOCKON CO.,LTD. All Rights Reserved. |
||
7 | * |
||
8 | * http://www.lockon.co.jp/ |
||
9 | * |
||
10 | * This program is free software; you can redistribute it and/or |
||
11 | * modify it under the terms of the GNU General Public License |
||
12 | * as published by the Free Software Foundation; either version 2 |
||
13 | * of the License, or (at your option) any later version. |
||
14 | * |
||
15 | * This program is distributed in the hope that it will be useful, |
||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
18 | * GNU General Public License for more details. |
||
19 | * |
||
20 | * You should have received a copy of the GNU General Public License |
||
21 | * along with this program; if not, write to the Free Software |
||
22 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
||
23 | */ |
||
24 | |||
25 | namespace Eccube\Service; |
||
26 | |||
27 | use Eccube\Common\Constant; |
||
28 | use Eccube\Exception\PluginException; |
||
29 | use Eccube\Util\Cache; |
||
30 | use Eccube\Util\Str; |
||
31 | use Symfony\Component\Filesystem\Filesystem; |
||
32 | use Symfony\Component\Yaml\Yaml; |
||
33 | |||
34 | class PluginService |
||
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
35 | { |
||
36 | const CONFIG_YML = 'config.yml'; |
||
37 | const EVENT_YML = 'event.yml'; |
||
38 | private $app; |
||
39 | 3 | ||
40 | public function __construct($app) |
||
0 ignored issues
–
show
|
|||
41 | 3 | { |
|
42 | 3 | $this->app = $app; |
|
43 | } |
||
44 | |||
45 | public function install($path, $source = 0) |
||
0 ignored issues
–
show
|
|||
46 | { |
||
47 | $pluginBaseDir = null; |
||
48 | $tmp = null; |
||
49 | |||
50 | try { |
||
51 | $this->app->removePluginConfigCache(); |
||
52 | Cache::clear($this->app, false); |
||
53 | $tmp = $this->createTempDir(); |
||
54 | |||
55 | $this->unpackPluginArchive($path, $tmp); //一旦テンポラリに展開 |
||
56 | $this->checkPluginArchiveContent($tmp); |
||
57 | |||
58 | $config = $this->readYml($tmp.'/'.self::CONFIG_YML); |
||
59 | $event = $this->readYml($tmp.'/'.self::EVENT_YML); |
||
60 | $this->deleteFile($tmp); // テンポラリのファイルを削除 |
||
61 | |||
62 | $this->checkSamePlugin($config['code']); // 重複していないかチェック |
||
63 | |||
64 | $pluginBaseDir = $this->calcPluginDir($config['code']); |
||
65 | $this->createPluginDir($pluginBaseDir); // 本来の置き場所を作成 |
||
66 | |||
67 | $this->unpackPluginArchive($path, $pluginBaseDir); // 問題なければ本当のplugindirへ |
||
68 | |||
69 | $this->registerPlugin($config, $event, $source); // dbにプラグイン登録 |
||
70 | $this->app->writePluginConfigCache(); |
||
71 | } catch (PluginException $e) { |
||
72 | $this->deleteDirs(array($tmp, $pluginBaseDir)); |
||
73 | throw $e; |
||
74 | } catch (\Exception $e) { // インストーラがどんなExceptionを上げるかわからないので |
||
75 | |||
76 | $this->deleteDirs(array($tmp, $pluginBaseDir)); |
||
77 | throw $e; |
||
78 | } |
||
79 | |||
80 | return true; |
||
81 | } |
||
82 | |||
83 | public function createTempDir() |
||
0 ignored issues
–
show
|
|||
84 | { |
||
85 | @mkdir($this->app['config']['plugin_temp_realdir']); |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
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...
|
|||
86 | $d = ($this->app['config']['plugin_temp_realdir'].'/'.sha1(Str::random(16))); |
||
87 | |||
88 | if (!mkdir($d, 0777)) { |
||
89 | throw new PluginException($php_errormsg.$d); |
||
90 | } |
||
91 | |||
92 | return $d; |
||
93 | } |
||
94 | |||
95 | public function deleteDirs($arr) |
||
0 ignored issues
–
show
|
|||
96 | { |
||
97 | foreach ($arr as $dir) { |
||
98 | if (file_exists($dir)) { |
||
99 | $fs = new Filesystem(); |
||
100 | $fs->remove($dir); |
||
101 | } |
||
102 | } |
||
103 | } |
||
104 | |||
105 | public function unpackPluginArchive($archive, $dir) |
||
0 ignored issues
–
show
|
|||
106 | { |
||
107 | $extension = pathinfo($archive, PATHINFO_EXTENSION); |
||
108 | try { |
||
109 | if ($extension == 'zip') { |
||
110 | $zip = new \ZipArchive(); |
||
111 | $zip->open($archive); |
||
112 | $zip->extractTo($dir); |
||
113 | $zip->close(); |
||
114 | } else { |
||
115 | $phar = new \PharData($archive); |
||
116 | $phar->extractTo($dir, null, true); |
||
117 | } |
||
118 | } catch (\Exception $e) { |
||
119 | throw new PluginException('アップロードに失敗しました。圧縮ファイルを確認してください。'); |
||
120 | } |
||
121 | } |
||
122 | |||
123 | public function checkPluginArchiveContent($dir, array $config_cache = array()) |
||
0 ignored issues
–
show
|
|||
124 | { |
||
125 | try { |
||
126 | if (!empty($config_cache)) { |
||
127 | $meta = $config_cache; |
||
128 | } else { |
||
129 | $meta = $this->readYml($dir . '/config.yml'); |
||
0 ignored issues
–
show
|
|||
130 | } |
||
131 | } catch (\Symfony\Component\Yaml\Exception\ParseException $e) { |
||
132 | throw new PluginException($e->getMessage(), $e->getCode(), $e); |
||
133 | } |
||
134 | |||
135 | if (!is_array($meta)) { |
||
136 | throw new PluginException('config.yml not found or syntax error'); |
||
137 | } |
||
138 | View Code Duplication | if (!isset($meta['code']) || !$this->checkSymbolName($meta['code'])) { |
|
139 | throw new PluginException('config.yml code empty or invalid_character(\W)'); |
||
140 | } |
||
141 | if (!isset($meta['name'])) { |
||
142 | // nameは直接クラス名やPATHに使われるわけではないため文字のチェックはなしし |
||
143 | throw new PluginException('config.yml name empty'); |
||
144 | } |
||
145 | View Code Duplication | if (isset($meta['event']) && !$this->checkSymbolName($meta['event'])) { // eventだけは必須ではない |
|
146 | throw new PluginException('config.yml event empty or invalid_character(\W) '); |
||
147 | } |
||
148 | if (!isset($meta['version'])) { |
||
149 | // versionは直接クラス名やPATHに使われるわけではないため文字のチェックはなしし |
||
150 | throw new PluginException('config.yml version invalid_character(\W) '); |
||
151 | } |
||
152 | if (isset($meta['orm.path'])) { |
||
153 | if (!is_array($meta['orm.path'])) { |
||
154 | throw new PluginException('config.yml orm.path invalid_character(\W) '); |
||
155 | } |
||
156 | } |
||
157 | if (isset($meta['service'])) { |
||
158 | if (!is_array($meta['service'])) { |
||
159 | throw new PluginException('config.yml service invalid_character(\W) '); |
||
160 | } |
||
161 | } |
||
162 | } |
||
163 | |||
164 | public function readYml($yml) |
||
0 ignored issues
–
show
|
|||
165 | { |
||
166 | if (file_exists($yml)) { |
||
167 | return Yaml::parse(file_get_contents($yml)); |
||
168 | } |
||
169 | |||
170 | return false; |
||
171 | } |
||
172 | |||
173 | public function checkSymbolName($string) |
||
0 ignored issues
–
show
|
|||
174 | { |
||
175 | return strlen($string) < 256 && preg_match('/^\w+$/', $string); |
||
176 | // plugin_nameやplugin_codeに使える文字のチェック |
||
177 | // a-z A-Z 0-9 _ |
||
178 | // ディレクトリ名などに使われれるので厳しめ |
||
179 | } |
||
180 | |||
181 | public function deleteFile($path) |
||
0 ignored issues
–
show
|
|||
182 | { |
||
183 | $f = new Filesystem(); |
||
184 | $f->remove($path); |
||
185 | } |
||
186 | |||
187 | public function checkSamePlugin($code) |
||
0 ignored issues
–
show
|
|||
188 | { |
||
189 | $repo = $this->app['eccube.repository.plugin']->findOneBy(array('code' => $code)); |
||
190 | if ($repo) { |
||
191 | throw new PluginException('plugin already installed.'); |
||
192 | } |
||
193 | } |
||
194 | |||
195 | public function calcPluginDir($name) |
||
0 ignored issues
–
show
|
|||
196 | { |
||
197 | return $this->app['config']['plugin_realdir'].'/'.$name; |
||
198 | } |
||
199 | |||
200 | public function createPluginDir($d) |
||
0 ignored issues
–
show
|
|||
201 | { |
||
202 | $b = @mkdir($d); |
||
203 | if (!$b) { |
||
204 | throw new PluginException($php_errormsg); |
||
205 | } |
||
206 | } |
||
207 | |||
208 | public function registerPlugin($meta, $event_yml, $source = 0) |
||
0 ignored issues
–
show
|
|||
209 | { |
||
210 | $em = $this->app['orm.em']; |
||
211 | $em->getConnection()->beginTransaction(); |
||
212 | try { |
||
213 | $p = new \Eccube\Entity\Plugin(); |
||
214 | // インストール直後はプラグインは有効にしない |
||
215 | $p->setName($meta['name']) |
||
216 | ->setEnable(Constant::DISABLED) |
||
217 | ->setClassName(isset($meta['event']) ? $meta['event'] : '') |
||
218 | ->setVersion($meta['version']) |
||
219 | ->setDelflg(Constant::DISABLED) |
||
220 | ->setSource($source) |
||
221 | ->setCode($meta['code']); |
||
222 | |||
223 | $em->persist($p); |
||
224 | $em->flush(); |
||
225 | |||
226 | if (is_array($event_yml)) { |
||
227 | foreach ($event_yml as $event => $handlers) { |
||
228 | foreach ($handlers as $handler) { |
||
229 | if (!$this->checkSymbolName($handler[0])) { |
||
230 | throw new PluginException('Handler name format error'); |
||
231 | } |
||
232 | $peh = new \Eccube\Entity\PluginEventHandler(); |
||
233 | $peh->setPlugin($p) |
||
234 | ->setEvent($event) |
||
235 | ->setdelFlg(Constant::DISABLED) |
||
236 | ->setHandler($handler[0]) |
||
237 | ->setHandlerType($handler[1]) |
||
238 | ->setPriority($this->app['eccube.repository.plugin_event_handler']->calcNewPriority($event, $handler[1])); |
||
239 | $em->persist($peh); |
||
240 | $em->flush(); |
||
241 | } |
||
242 | } |
||
243 | } |
||
244 | |||
245 | $em->persist($p); |
||
246 | |||
247 | $this->callPluginManagerMethod($meta, 'install'); |
||
248 | |||
249 | $em->flush(); |
||
250 | $em->getConnection()->commit(); |
||
251 | } catch (\Exception $e) { |
||
252 | $em->getConnection()->rollback(); |
||
253 | throw new PluginException($e->getMessage()); |
||
254 | } |
||
255 | |||
256 | return $p; |
||
257 | } |
||
258 | |||
259 | public function callPluginManagerMethod($meta, $method) |
||
0 ignored issues
–
show
|
|||
260 | { |
||
261 | $class = '\\Plugin'.'\\'.$meta['code'].'\\'.'PluginManager'; |
||
262 | if (class_exists($class)) { |
||
263 | $installer = new $class(); // マネージャクラスに所定のメソッドがある場合だけ実行する |
||
264 | if (method_exists($installer, $method)) { |
||
265 | $installer->$method($meta, $this->app); |
||
266 | } |
||
267 | } |
||
268 | } |
||
269 | |||
270 | public function uninstall(\Eccube\Entity\Plugin $plugin) |
||
0 ignored issues
–
show
|
|||
271 | { |
||
272 | $pluginDir = $this->calcPluginDir($plugin->getCode()); |
||
273 | $this->app->removePluginConfigCache(); |
||
274 | Cache::clear($this->app, false); |
||
275 | $this->callPluginManagerMethod(Yaml::parse(file_get_contents($pluginDir.'/'.self::CONFIG_YML)), 'disable'); |
||
276 | $this->callPluginManagerMethod(Yaml::parse(file_get_contents($pluginDir.'/'.self::CONFIG_YML)), 'uninstall'); |
||
277 | $this->unregisterPlugin($plugin); |
||
278 | $this->deleteFile($pluginDir); |
||
279 | $this->app->writePluginConfigCache(); |
||
280 | return true; |
||
0 ignored issues
–
show
|
|||
281 | } |
||
282 | |||
283 | public function unregisterPlugin(\Eccube\Entity\Plugin $p) |
||
0 ignored issues
–
show
|
|||
284 | { |
||
285 | try { |
||
286 | $em = $this->app['orm.em']; |
||
287 | $em->getConnection()->beginTransaction(); |
||
288 | |||
289 | $p->setDelFlg(Constant::ENABLED)->setEnable(Constant::DISABLED); |
||
290 | |||
291 | foreach ($p->getPluginEventHandlers()->toArray() as $peh) { |
||
292 | $peh->setDelFlg(Constant::ENABLED); |
||
293 | } |
||
294 | |||
295 | $em->persist($p); |
||
296 | $em->flush(); |
||
297 | $em->getConnection()->commit(); |
||
298 | } catch (\Exception $e) { |
||
299 | $em->getConnection()->rollback(); |
||
300 | throw $e; |
||
301 | } |
||
302 | } |
||
303 | |||
304 | public function disable(\Eccube\Entity\Plugin $plugin) |
||
0 ignored issues
–
show
|
|||
305 | { |
||
306 | return $this->enable($plugin, false); |
||
307 | } |
||
308 | |||
309 | public function enable(\Eccube\Entity\Plugin $plugin, $enable = true) |
||
0 ignored issues
–
show
|
|||
310 | { |
||
311 | $em = $this->app['orm.em']; |
||
312 | try { |
||
313 | $this->app->removePluginConfigCache(); |
||
314 | Cache::clear($this->app, false); |
||
315 | $pluginDir = $this->calcPluginDir($plugin->getCode()); |
||
316 | $em->getConnection()->beginTransaction(); |
||
317 | $plugin->setEnable($enable ? Constant::ENABLED : Constant::DISABLED); |
||
318 | $em->persist($plugin); |
||
319 | $this->callPluginManagerMethod(Yaml::parse(file_get_contents($pluginDir.'/'.self::CONFIG_YML)), $enable ? 'enable' : 'disable'); |
||
320 | $em->flush(); |
||
321 | $em->getConnection()->commit(); |
||
322 | $this->app->writePluginConfigCache(); |
||
323 | } catch (\Exception $e) { |
||
324 | $em->getConnection()->rollback(); |
||
325 | throw $e; |
||
326 | } |
||
327 | |||
328 | return true; |
||
329 | } |
||
330 | |||
331 | public function update(\Eccube\Entity\Plugin $plugin, $path) |
||
0 ignored issues
–
show
|
|||
332 | { |
||
333 | $pluginBaseDir = null; |
||
334 | $tmp = null; |
||
335 | try { |
||
336 | $this->app->removePluginConfigCache(); |
||
337 | Cache::clear($this->app, false); |
||
338 | $tmp = $this->createTempDir(); |
||
339 | |||
340 | $this->unpackPluginArchive($path, $tmp); //一旦テンポラリに展開 |
||
341 | $this->checkPluginArchiveContent($tmp); |
||
342 | |||
343 | $config = $this->readYml($tmp.'/'.self::CONFIG_YML); |
||
344 | $event = $this->readYml($tmp.'/event.yml'); |
||
345 | |||
346 | if ($plugin->getCode() != $config['code']) { |
||
347 | throw new PluginException('new/old plugin code is different.'); |
||
348 | } |
||
349 | |||
350 | $pluginBaseDir = $this->calcPluginDir($config['code']); |
||
351 | $this->deleteFile($tmp); // テンポラリのファイルを削除 |
||
352 | |||
353 | $this->unpackPluginArchive($path, $pluginBaseDir); // 問題なければ本当のplugindirへ |
||
354 | |||
355 | $this->updatePlugin($plugin, $config, $event); // dbにプラグイン登録 |
||
356 | $this->app->writePluginConfigCache(); |
||
357 | } catch (PluginException $e) { |
||
358 | foreach (array($tmp) as $dir) { |
||
359 | if (file_exists($dir)) { |
||
360 | $fs = new Filesystem(); |
||
361 | $fs->remove($dir); |
||
362 | } |
||
363 | } |
||
364 | throw $e; |
||
365 | } |
||
366 | |||
367 | return true; |
||
368 | } |
||
369 | |||
370 | public function updatePlugin(\Eccube\Entity\Plugin $plugin, $meta, $event_yml) |
||
0 ignored issues
–
show
|
|||
371 | { |
||
372 | try { |
||
373 | $em = $this->app['orm.em']; |
||
374 | $em->getConnection()->beginTransaction(); |
||
375 | $plugin->setVersion($meta['version']) |
||
376 | ->setName($meta['name']); |
||
377 | |||
378 | if (isset($meta['event'])) { |
||
379 | $plugin->setClassName($meta['event']); |
||
380 | } |
||
381 | |||
382 | $rep = $this->app['eccube.repository.plugin_event_handler']; |
||
383 | |||
384 | if (is_array($event_yml)) { |
||
385 | foreach ($event_yml as $event => $handlers) { |
||
386 | foreach ($handlers as $handler) { |
||
387 | if (!$this->checkSymbolName($handler[0])) { |
||
388 | throw new PluginException('Handler name format error'); |
||
389 | } |
||
390 | // updateで追加されたハンドラかどうか調べる |
||
391 | $peh = $rep->findBy(array('del_flg' => Constant::DISABLED, |
||
392 | 'plugin_id' => $plugin->getId(), |
||
393 | 'event' => $event, |
||
394 | 'handler' => $handler[0], |
||
395 | 'handler_type' => $handler[1],)); |
||
0 ignored issues
–
show
|
|||
396 | |||
397 | if (!$peh) { // 新規にevent.ymlに定義されたハンドラなのでinsertする |
||
398 | $peh = new \Eccube\Entity\PluginEventHandler(); |
||
399 | $peh->setPlugin($plugin) |
||
400 | ->setEvent($event) |
||
401 | ->setdelFlg(Constant::DISABLED) |
||
402 | ->setHandler($handler[0]) |
||
403 | ->setHandlerType($handler[1]) |
||
404 | ->setPriority($rep->calcNewPriority($event, $handler[1])); |
||
405 | $em->persist($peh); |
||
406 | $em->flush(); |
||
407 | } |
||
408 | } |
||
409 | } |
||
410 | |||
411 | # アップデート後のevent.ymlで削除されたハンドラをdtb_plugin_event_handlerから探して削除 |
||
0 ignored issues
–
show
|
|||
412 | foreach ($rep->findBy(array('del_flg' => Constant::DISABLED, 'plugin_id' => $plugin->getId())) as $peh) { |
||
413 | if (!isset($event_yml[$peh->getEvent()])) { |
||
414 | $em->remove($peh); |
||
415 | $em->flush(); |
||
416 | } else { |
||
417 | $match = false; |
||
418 | foreach ($event_yml[$peh->getEvent()] as $handler) { |
||
419 | if ($peh->getHandler() == $handler[0] && $peh->getHandlerType() == $handler[1]) { |
||
420 | $match = true; |
||
421 | } |
||
422 | } |
||
423 | if (!$match) { |
||
424 | $em->remove($peh); |
||
425 | $em->flush(); |
||
426 | } |
||
427 | } |
||
428 | } |
||
429 | } |
||
430 | |||
431 | $em->persist($plugin); |
||
432 | $this->callPluginManagerMethod($meta, 'update'); |
||
433 | $em->flush(); |
||
434 | $em->getConnection()->commit(); |
||
435 | } catch (\Exception $e) { |
||
436 | $em->getConnection()->rollback(); |
||
437 | throw $e; |
||
438 | } |
||
439 | } |
||
440 | } |
||
441 |