GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (4873)

Security Analysis    not enabled

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/common/plugin/PluginManager.class.php (2 issues)

Labels
Severity

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
 * 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
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, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
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, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
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