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 | * Copyright (c) Enalean SAS, 2015. All rights reserved |
||
4 | * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved |
||
5 | * |
||
6 | * This file is a part of Codendi. |
||
7 | * |
||
8 | * Codendi is free software; you can redistribute it and/or modify |
||
9 | * it under the terms of the GNU General Public License as published by |
||
10 | * the Free Software Foundation; either version 2 of the License, or |
||
11 | * (at your option) any later version. |
||
12 | * |
||
13 | * Codendi is distributed in the hope that it will be useful, |
||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
16 | * GNU General Public License for more details. |
||
17 | * |
||
18 | * You should have received a copy of the GNU General Public License |
||
19 | * along with Codendi. If not, see <http://www.gnu.org/licenses/>. |
||
20 | */ |
||
21 | |||
22 | /** |
||
23 | * PluginManager |
||
24 | */ |
||
25 | class PluginManager { |
||
26 | |||
27 | |||
28 | const PLUGIN_HOOK_CACHE_FILE = 'hooks.json'; |
||
29 | |||
30 | /** @var EventManager */ |
||
31 | private $event_manager; |
||
32 | |||
33 | /** @var PluginFactory */ |
||
34 | private $plugin_factory; |
||
35 | |||
36 | /** @var SiteCache */ |
||
37 | private $site_cache; |
||
38 | |||
39 | /** @var PluginManager */ |
||
40 | private static $instance = null; |
||
41 | |||
42 | /** @var ForgeUpgradeConfig */ |
||
43 | private $forgeupgrade_config; |
||
44 | |||
45 | var $pluginHookPriorityManager; |
||
46 | |||
47 | public function __construct(PluginFactory $plugin_factory, EventManager $event_manager, SiteCache $site_cache, ForgeUpgradeConfig $forgeupgrade_config) { |
||
48 | $this->plugin_factory = $plugin_factory; |
||
49 | $this->event_manager = $event_manager; |
||
50 | $this->site_cache = $site_cache; |
||
51 | $this->forgeupgrade_config = $forgeupgrade_config; |
||
52 | } |
||
53 | |||
54 | public static function instance() { |
||
55 | if (! self::$instance) { |
||
56 | self::$instance = new PluginManager( |
||
57 | PluginFactory::instance(), |
||
58 | EventManager::instance(), |
||
59 | new SiteCache(), |
||
60 | new ForgeUpgradeConfig( |
||
61 | new System_Command() |
||
62 | ) |
||
63 | ); |
||
64 | } |
||
65 | return self::$instance; |
||
66 | } |
||
67 | |||
68 | public static function setInstance(PluginManager $plugin_manager) { |
||
69 | self::$instance = $plugin_manager; |
||
70 | } |
||
71 | |||
72 | public static function clearInstance() { |
||
73 | self::$instance = null; |
||
74 | } |
||
75 | |||
76 | public function loadPlugins() { |
||
77 | foreach ($this->getHooksCache() as $plugin) { |
||
78 | $this->loadPluginFiles($plugin['path']); |
||
79 | $proxy = new PluginProxy($plugin['class'], $plugin['id']); |
||
80 | foreach ($plugin['hooks'] as $hook) { |
||
81 | $this->addListener($hook, $proxy); |
||
82 | } |
||
83 | } |
||
84 | } |
||
85 | |||
86 | private function loadPluginFiles($path) { |
||
87 | include_once $path; |
||
88 | $autoload = dirname($path).'/autoload.php'; |
||
89 | if (file_exists($autoload)) { |
||
90 | include_once $autoload; |
||
91 | } |
||
92 | } |
||
93 | |||
94 | private function addListener($hook, PluginProxy $proxy) { |
||
95 | $proxy->addListener( |
||
96 | $hook['event'], |
||
97 | $hook['callback'], |
||
98 | $hook['recall_event'] |
||
99 | ); |
||
100 | $this->event_manager->addListener( |
||
101 | $hook['event'], |
||
102 | $proxy, |
||
103 | 'processEvent', |
||
104 | true |
||
105 | ); |
||
106 | } |
||
107 | |||
108 | private function getHooksCache() { |
||
109 | $hooks_cache_file = $this->getCacheFile(); |
||
110 | if (! file_exists($hooks_cache_file)) { |
||
111 | $hooks_cache = $this->getHooksOfAvailablePlugins(); |
||
112 | file_put_contents($hooks_cache_file, json_encode($hooks_cache)); |
||
113 | return $hooks_cache; |
||
114 | } |
||
115 | return json_decode(file_get_contents($hooks_cache_file), true); |
||
116 | } |
||
117 | |||
118 | public function getCacheFile() { |
||
119 | return ForgeConfig::get('codendi_cache_dir').'/'.self::PLUGIN_HOOK_CACHE_FILE; |
||
120 | } |
||
121 | |||
122 | private function getHooksOfAvailablePlugins() { |
||
123 | $hooks_cache = array(); |
||
124 | foreach($this->plugin_factory->getAvailablePlugins() as $plugin) { |
||
125 | $hooks_cache[$plugin->getName()] = array( |
||
126 | 'id' => $plugin->getId(), |
||
127 | 'class' => $this->plugin_factory->getClassName($plugin->getName()), |
||
128 | 'path' => $this->plugin_factory->getClassPath($plugin->getName()), |
||
129 | 'hooks' => array() |
||
130 | ); |
||
131 | foreach ($plugin->getHooksAndCallbacks()->iterator() as $hook) { |
||
132 | $hooks_cache[$plugin->getName()]['hooks'][] = array( |
||
133 | 'event' => $hook['hook'], |
||
134 | 'callback' => $hook['callback'], |
||
135 | 'recall_event' => $hook['recallHook'], |
||
136 | ); |
||
137 | } |
||
138 | } |
||
139 | return $hooks_cache; |
||
140 | } |
||
141 | |||
142 | public function getAvailablePlugins() { |
||
143 | return $this->plugin_factory->getAvailablePlugins(); |
||
144 | } |
||
145 | |||
146 | function getAllPlugins() { |
||
147 | return $this->plugin_factory->getAllPlugins(); |
||
148 | } |
||
149 | |||
150 | function isPluginAvailable($plugin) { |
||
151 | return $this->plugin_factory->isPluginAvailable($plugin); |
||
152 | } |
||
153 | |||
154 | function availablePlugin($plugin) { |
||
155 | if ($plugin->canBeMadeAvailable()) { |
||
156 | $this->plugin_factory->availablePlugin($plugin); |
||
157 | |||
158 | $plugin->setAvailable(true); |
||
159 | $this->site_cache->invalidatePluginBasedCaches(); |
||
160 | } |
||
161 | } |
||
162 | |||
163 | function unavailablePlugin($plugin) { |
||
164 | $this->plugin_factory->unavailablePlugin($plugin); |
||
165 | |||
166 | $plugin->setAvailable(false); |
||
167 | $this->site_cache->invalidatePluginBasedCaches(); |
||
168 | } |
||
169 | |||
170 | public function installAndActivate($name) { |
||
171 | $plugin = $this->plugin_factory->getPluginByName($name); |
||
172 | if (! $plugin) { |
||
173 | $plugin = $this->installPlugin($name); |
||
174 | } |
||
175 | if (! $this->plugin_factory->isPluginAvailable($plugin)) { |
||
176 | $this->plugin_factory->availablePlugin($plugin); |
||
177 | } |
||
178 | $this->site_cache->invalidatePluginBasedCaches(); |
||
179 | } |
||
180 | |||
181 | function installPlugin($name) { |
||
182 | $plugin = false; |
||
183 | if ($this->isNameValid($name)) { |
||
184 | if (!$this->plugin_factory->isPluginInstalled($name)) { |
||
185 | if (!$this->_executeSqlStatements('install', $name)) { |
||
186 | $plugin = $this->plugin_factory->createPlugin($name); |
||
187 | if ($plugin instanceof Plugin) { |
||
188 | $this->_createEtc($name); |
||
189 | $this->configureForgeUpgrade($name); |
||
190 | $plugin->postInstall(); |
||
191 | } else { |
||
192 | $GLOBALS['Response']->addFeedback('error', 'Unable to create plugin'); |
||
193 | } |
||
194 | } else { |
||
195 | $GLOBALS['Response']->addFeedback('error', 'DB may be corrupted'); |
||
196 | } |
||
197 | } |
||
198 | } |
||
199 | return $plugin; |
||
200 | } |
||
201 | |||
202 | function uninstallPlugin($plugin) { |
||
203 | $name = $this->plugin_factory->getNameForPlugin($plugin); |
||
204 | if (!$this->_executeSqlStatements('uninstall', $name)) { |
||
205 | $this->uninstallForgeUpgrade($name); |
||
206 | return $this->plugin_factory->removePlugin($plugin); |
||
207 | } else { |
||
208 | return false; |
||
209 | } |
||
210 | } |
||
211 | function getPostInstall($name) { |
||
212 | $path_to_file = '/'.$name.'/POSTINSTALL.txt'; |
||
213 | return file_exists($GLOBALS['sys_pluginsroot'].$path_to_file) ? |
||
214 | file_get_contents($GLOBALS['sys_pluginsroot'].$path_to_file) : |
||
215 | false; |
||
216 | } |
||
217 | |||
218 | function getInstallReadme($name) { |
||
219 | foreach ($this->plugin_factory->getAllPossiblePluginsDir() as $dir) { |
||
220 | $path = $dir.'/'.$name; |
||
221 | if (file_exists($path.'/README.mkd') || file_exists($path.'/README')) { |
||
222 | return $path.'/README'; |
||
223 | } |
||
224 | } |
||
225 | return false; |
||
226 | } |
||
227 | |||
228 | /** |
||
229 | * Format the readme file of a plugin |
||
230 | * |
||
231 | * Use markdown formatter if installed and if README.mkd exists |
||
232 | * Otherwise assume text/plain and put it in <pre> tags |
||
233 | * If README file doesn't exist, return empty string. |
||
234 | * |
||
235 | * For Markdown, the following is needed: |
||
236 | * <code> |
||
237 | * pear channel-discover pear.michelf.com |
||
238 | * pear install michelf/package |
||
239 | * </code> |
||
240 | * |
||
241 | * @return string html |
||
242 | */ |
||
243 | function fetchFormattedReadme($file) { |
||
244 | if (is_file("$file.mkd")) { |
||
245 | $content = file_get_contents("$file.mkd"); |
||
246 | if (@include_once "markdown.php") { |
||
247 | return Markdown($content); |
||
248 | } |
||
249 | return $this->getEscapedReadme($content); |
||
250 | } |
||
251 | |||
252 | if (is_file("$file.txt")) { |
||
253 | return $this->getEscapedReadme(file_get_contents("$file.txt")); |
||
254 | } |
||
255 | |||
256 | if (is_file($file)) { |
||
257 | return $this->getEscapedReadme(file_get_contents($file)); |
||
258 | } |
||
259 | |||
260 | return ''; |
||
261 | } |
||
262 | |||
263 | private function getEscapedReadme($content) { |
||
264 | return '<pre>'.Codendi_HTMLPurifier::instance()->purify($content).'</pre>'; |
||
265 | } |
||
266 | |||
267 | /** |
||
268 | * Initialize ForgeUpgrade configuration for given plugin |
||
269 | * |
||
270 | * Add in configuration and record existing migration scripts as 'skipped' |
||
271 | * because the 'install.sql' script is up-to-date with latest DB modif. |
||
272 | * |
||
273 | * @param String $name Plugin's name |
||
274 | */ |
||
275 | protected function configureForgeUpgrade($name) { |
||
276 | try { |
||
277 | $plugin_path = $GLOBALS['sys_pluginsroot'].$name; |
||
278 | $this->forgeupgrade_config->loadDefaults(); |
||
279 | $this->forgeupgrade_config->addPath($GLOBALS['sys_pluginsroot'].$name); |
||
280 | $this->forgeupgrade_config->recordOnlyPath($plugin_path); |
||
281 | } catch (Exception $e) { |
||
282 | $GLOBALS['Response']->addFeedback('warning', "ForgeUpgrade configuration update failed: ".$e->getMessage()); |
||
283 | } |
||
284 | } |
||
285 | |||
286 | /** |
||
287 | * Remove plugin from ForgeUpgrade configuration |
||
288 | * |
||
289 | * Keep migration scripts in DB, it doesn't matter. |
||
290 | * |
||
291 | * @param String $name Plugin's name |
||
292 | */ |
||
293 | protected function uninstallForgeUpgrade($name) { |
||
294 | try { |
||
295 | $this->forgeupgrade_config->loadDefaults(); |
||
296 | $this->forgeupgrade_config->removePath($GLOBALS['sys_pluginsroot'].$name); |
||
297 | } catch (Exception $e) { |
||
298 | $GLOBALS['Response']->addFeedback('warning', "ForgeUpgrade configuration update failed: ".$e->getMessage()); |
||
299 | } |
||
300 | } |
||
301 | |||
302 | function _createEtc($name) { |
||
303 | if (!is_dir($GLOBALS['sys_custompluginsroot'] .'/'. $name)) { |
||
304 | mkdir($GLOBALS['sys_custompluginsroot'] .'/'. $name, 0700); |
||
305 | } |
||
306 | if (is_dir($GLOBALS['sys_pluginsroot'] .'/'. $name .'/etc')) { |
||
307 | if (!is_dir($GLOBALS['sys_custompluginsroot'] .'/'. $name .'/etc')) { |
||
308 | mkdir($GLOBALS['sys_custompluginsroot'] .'/'. $name . '/etc', 0700); |
||
309 | } |
||
310 | $etcs = glob($GLOBALS['sys_pluginsroot'] .'/'. $name .'/etc/*'); |
||
311 | foreach($etcs as $etc) { |
||
312 | if(is_dir($etc)) { |
||
313 | $this->copyDirectory($etc, $GLOBALS['sys_custompluginsroot'] .'/'. $name . '/etc/' . basename($etc)); |
||
314 | } else { |
||
315 | copy($etc, $GLOBALS['sys_custompluginsroot'] .'/'. $name . '/etc/' . basename($etc)); |
||
316 | } |
||
317 | } |
||
318 | $incdists = glob($GLOBALS['sys_custompluginsroot'] .'/'. $name .'/etc/*.dist'); |
||
319 | foreach($incdists as $incdist) { |
||
320 | rename($incdist, $GLOBALS['sys_custompluginsroot'] .'/'. $name . '/etc/' . basename($incdist, '.dist')); |
||
321 | } |
||
322 | } |
||
323 | } |
||
324 | |||
325 | function _executeSqlStatements($file, $name) { |
||
326 | $db_corrupted = false; |
||
327 | $path_to_file = '/'.$name.'/db/'.$file.'.sql'; |
||
328 | |||
329 | foreach ($this->plugin_factory->getAllPossiblePluginsDir() as $dir) { |
||
330 | $sql_filename = $dir.$path_to_file; |
||
331 | if (file_exists($sql_filename)) { |
||
332 | $dbtables = new DBTablesDAO(); |
||
333 | if (!$dbtables->updateFromFile($sql_filename)) { |
||
334 | $db_corrupted = true; |
||
335 | } |
||
336 | } |
||
337 | } |
||
338 | |||
339 | return $db_corrupted; |
||
340 | } |
||
341 | |||
342 | function getNotYetInstalledPlugins() { |
||
343 | return $this->plugin_factory->getNotYetInstalledPlugins(); |
||
344 | } |
||
345 | |||
346 | function isNameValid($name) { |
||
347 | return (0 === preg_match('/[^a-zA-Z0-9_-]/', $name)); |
||
348 | } |
||
349 | |||
350 | function getPluginByName($name) { |
||
351 | return $this->plugin_factory->getPluginByName($name); |
||
352 | } |
||
353 | |||
354 | function getAvailablePluginByName($name) { |
||
355 | $plugin = $this->getPluginByName($name); |
||
356 | if ($plugin && $this->isPluginAvailable($plugin)) { |
||
357 | return $plugin; |
||
358 | } |
||
359 | } |
||
360 | function getPluginById($id) { |
||
361 | return $this->plugin_factory->getPluginById($id); |
||
362 | } |
||
363 | function pluginIsCustom($plugin) { |
||
364 | return $this->plugin_factory->pluginIsCustom($plugin); |
||
365 | } |
||
366 | |||
367 | var $plugins_name; |
||
368 | function getNameForPlugin($plugin) { |
||
369 | if (!$this->plugins_name) { |
||
370 | $this->plugins_name = array(); |
||
371 | } |
||
372 | if (!isset($this->plugins_name[$plugin->getId()])) { |
||
373 | $this->plugins_name[$plugin->getId()] = $this->plugin_factory->getNameForPlugin($plugin); |
||
374 | } |
||
375 | return $this->plugins_name[$plugin->getId()]; |
||
376 | } |
||
377 | |||
378 | function getAllowedProjects($plugin) { |
||
379 | return $this->plugin_factory->getProjectsByPluginId($plugin); |
||
380 | } |
||
381 | |||
382 | function _updateProjectForPlugin($action, $plugin, $projectIds) { |
||
383 | $success = true; |
||
384 | $successOnce = false; |
||
385 | |||
386 | if(is_array($projectIds)) { |
||
387 | foreach($projectIds as $prjId) { |
||
388 | switch($action){ |
||
389 | case 'add': |
||
390 | $success = $success && $this->plugin_factory->addProjectForPlugin($plugin, $prjId); |
||
391 | break; |
||
392 | case 'del': |
||
393 | $success = $success && $this->plugin_factory->delProjectForPlugin($plugin, $prjId); |
||
394 | break; |
||
395 | } |
||
396 | |||
397 | if($success === true) |
||
398 | $successOnce = true; |
||
399 | } |
||
400 | } |
||
401 | elseif(is_numeric($projectIds)) { |
||
402 | switch($action){ |
||
403 | case 'add': |
||
404 | $success = $success && $this->plugin_factory->addProjectForPlugin($plugin, $prjId); |
||
0 ignored issues
–
show
|
|||
405 | break; |
||
406 | case 'del': |
||
407 | $success = $success && $this->plugin_factory->delProjectForPlugin($plugin, $prjId); |
||
0 ignored issues
–
show
The variable
$prjId seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?
This error can happen if you refactor code and forget to move the variable initialization. Let’s take a look at a simple example: function someFunction() {
$x = 5;
echo $x;
}
The above code is perfectly fine. Now imagine that we re-order the statements: function someFunction() {
echo $x;
$x = 5;
}
In that case, ![]() |
|||
408 | break; |
||
409 | } |
||
410 | $successOnce = $success; |
||
411 | } |
||
412 | |||
413 | if($successOnce && ($action == 'add')) { |
||
414 | $this->plugin_factory->restrictProjectPluginUse($plugin, true); |
||
415 | } |
||
416 | } |
||
417 | |||
418 | function addProjectForPlugin($plugin, $projectIds) { |
||
419 | $this->_updateProjectForPlugin('add', $plugin, $projectIds); |
||
420 | } |
||
421 | |||
422 | function delProjectForPlugin($plugin, $projectIds) { |
||
423 | $this->_updateProjectForPlugin('del', $plugin, $projectIds); |
||
424 | } |
||
425 | |||
426 | function isProjectPluginRestricted($plugin) { |
||
427 | return $this->plugin_factory->isProjectPluginRestricted($plugin); |
||
428 | } |
||
429 | |||
430 | function updateProjectPluginRestriction($plugin, $restricted) { |
||
431 | $this->plugin_factory->restrictProjectPluginUse($plugin, $restricted); |
||
432 | if($restricted == false) { |
||
433 | $this->plugin_factory->truncateProjectPlugin($plugin); |
||
434 | } |
||
435 | } |
||
436 | |||
437 | function isPluginAllowedForProject($plugin, $projectId) { |
||
438 | if($this->isProjectPluginRestricted($plugin)) { |
||
439 | return $this->plugin_factory->isPluginAllowedForProject($plugin, $projectId); |
||
440 | } |
||
441 | else { |
||
442 | return true; |
||
443 | } |
||
444 | } |
||
445 | |||
446 | /** |
||
447 | * This method instantiate a plugin that should not be used outside |
||
448 | * of installation use case. It bypass all caches and do not check availability |
||
449 | * of the plugin. |
||
450 | * |
||
451 | * @param string $name The name of the plugin (docman, tracker, …) |
||
452 | * @return Plugin |
||
453 | */ |
||
454 | public function getPluginDuringInstall($name) { |
||
455 | return $this->plugin_factory->instantiatePlugin(0, $name); |
||
456 | } |
||
457 | |||
458 | private function copyDirectory($source, $destination) { |
||
459 | |||
460 | if(!is_dir($destination)) { |
||
461 | if(!mkdir($destination)) { |
||
462 | return false; |
||
463 | } |
||
464 | } |
||
465 | |||
466 | $iterator = new DirectoryIterator($source); |
||
467 | foreach($iterator as $file) { |
||
468 | if($file->isFile()) { |
||
469 | copy($file->getRealPath(), "$destination/" . $file->getFilename()); |
||
470 | } else if(!$file->isDot() && $file->isDir()) { |
||
471 | $this->copyDirectory($file->getRealPath(), "$destination/$file"); |
||
472 | } |
||
473 | } |
||
474 | } |
||
475 | |||
476 | public function invalidateCache() { |
||
477 | if (file_exists($this->getCacheFile())) { |
||
478 | unlink($this->getCacheFile()); |
||
479 | } |
||
480 | } |
||
481 | } |
||
482 |
This error can happen if you refactor code and forget to move the variable initialization.
Let’s take a look at a simple example:
The above code is perfectly fine. Now imagine that we re-order the statements:
In that case,
$x
would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.