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.

plugins/git/include/Git.class.php (6 issues)

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
/**
4
  * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved
5
  * Copyright (c) Enalean, 2011-2016. All Rights Reserved.
6
  *
7
  * This file is a part of Tuleap.
8
  *
9
  * Tuleap is free software; you can redistribute it and/or modify
10
  * it under the terms of the GNU General Public License as published by
11
  * the Free Software Foundation; either version 2 of the License, or
12
  * (at your option) any later version.
13
  *
14
  * Tuleap is distributed in the hope that it will be useful,
15
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
  * GNU General Public License for more details.
18
  *
19
  * You should have received a copy of the GNU General Public License
20
  * along with Tuleap. If not, see <http://www.gnu.org/licenses/
21
  */
22
23
require_once('common/valid/ValidFactory.class.php');
24
25
/**
26
 * Git
27
 * @author Guillaume Storchi
28
 */
29
class Git extends PluginController {
30
31
    const PERM_READ  = 'PLUGIN_GIT_READ';
32
    const PERM_WRITE = 'PLUGIN_GIT_WRITE';
33
    const PERM_WPLUS = 'PLUGIN_GIT_WPLUS';
34
35
    const PERM_ADMIN         = 'PLUGIN_GIT_ADMIN';
36
    const SPECIAL_PERM_ADMIN = 'PROJECT_ADMIN';
37
38
    const SCOPE_PERSONAL = 'personal';
39
40
    const REFERENCE_KEYWORD = 'git';
41
    const REFERENCE_NATURE  = 'git_commit';
42
43
    /**
44
     * Lists all git-related permission types.
45
     *
46
     * @return array
47
     */
48
    public static function allPermissionTypes() {
49
        return array(Git::PERM_READ, Git::PERM_WRITE, Git::PERM_WPLUS);
50
    }
51
52
    /**
53
     * @var Git_Backend_Gitolite
54
     */
55
    private $backend_gitolite;
56
57
    /**
58
     * @var Git_Mirror_MirrorDataMapper
59
     */
60
    private $mirror_data_mapper;
61
62
    /**
63
     * @var Logger
64
     */
65
    private $logger;
66
67
    /**
68
     * @var int
69
     */
70
    protected $groupId;
71
72
    /**
73
     * @var GitRepositoryFactory
74
     */
75
    protected $factory;
76
77
    /**
78
     * @var UserManager
79
     */
80
    private $userManager;
81
82
    /**
83
     * @var ProjectManager
84
     */
85
    private $projectManager;
86
87
    /**
88
     * @var GitPlugin
89
     */
90
    private $plugin;
91
92
    /**
93
     * @var Git_RemoteServer_GerritServerFactory
94
     */
95
    private $gerrit_server_factory;
96
97
    /** @var Git_Driver_Gerrit_GerritDriverFactory */
98
    private $driver_factory;
99
100
    /** @var GitRepositoryManager */
101
    private $repository_manager;
102
103
    /** @var Git_SystemEventManager */
104
    private $git_system_event_manager;
105
106
    /** @var Git_Driver_Gerrit_UserAccountManager */
107
    private $gerrit_usermanager;
108
109
    /** @var PluginManager */
110
    private $plugin_manager;
111
112
    /** @var Git_Driver_Gerrit_ProjectCreator */
113
    private $project_creator;
114
115
    /** @var GitPermissionsManager */
116
    private $permissions_manager;
117
118
    /** @var Git_GitRepositoryUrlManager */
119
    private $url_manager;
120
121
    /** @var Project */
122
    private $project;
123
124
    public function __construct(
125
        GitPlugin $plugin,
126
        Git_RemoteServer_GerritServerFactory $gerrit_server_factory,
127
        Git_Driver_Gerrit_GerritDriverFactory $driver_factory,
128
        GitRepositoryManager $repository_manager,
129
        Git_SystemEventManager $system_event_manager,
130
        Git_Driver_Gerrit_UserAccountManager $gerrit_usermanager,
131
        GitRepositoryFactory $git_repository_factory,
132
        UserManager $user_manager,
133
        ProjectManager $project_manager,
134
        PluginManager $plugin_manager,
135
        Codendi_Request $request,
136
        Git_Driver_Gerrit_ProjectCreator $project_creator,
137
        Git_Driver_Gerrit_Template_TemplateFactory $template_factory,
138
        GitPermissionsManager $permissions_manager,
139
        Git_GitRepositoryUrlManager $url_manager,
140
        Logger $logger,
141
        Git_Backend_Gitolite $backend_gitolite,
142
        Git_Mirror_MirrorDataMapper $mirror_data_mapper
143
    ) {
144
        parent::__construct($user_manager, $request);
145
146
        $this->userManager              = $user_manager;
147
        $this->projectManager           = $project_manager;
148
        $this->factory                  = $git_repository_factory;
149
        $this->gerrit_server_factory    = $gerrit_server_factory;
150
        $this->driver_factory           = $driver_factory;
151
        $this->repository_manager       = $repository_manager;
152
        $this->git_system_event_manager = $system_event_manager;
153
        $this->gerrit_usermanager       = $gerrit_usermanager;
154
        $this->plugin_manager           = $plugin_manager;
155
        $this->project_creator          = $project_creator;
156
        $this->template_factory         = $template_factory;
157
        $this->permissions_manager      = $permissions_manager;
158
        $this->plugin                   = $plugin;
159
        $this->url_manager              = $url_manager;
160
        $this->logger                   = $logger;
161
        $this->backend_gitolite         = $backend_gitolite;
162
        $this->mirror_data_mapper       = $mirror_data_mapper;
163
164
        $url = new Git_URL(
165
            $this->projectManager,
166
            $this->factory,
167
            $_SERVER['REQUEST_URI']
168
        );
169
        $this->routeGitSmartHTTP($url);
170
        $this->routeUsingFriendlyURLs($url);
171
        $this->routeUsingStandardURLs($url);
172
173
        $valid = new Valid_GroupId('group_id');
174
        $valid->required();
175
        if($this->request->valid($valid)) {
176
            $this->groupId = (int)$this->request->get('group_id');
177
        }
178
        $valid = new Valid_String('action');
179
        $valid->required();
180
        if($this->request->valid($valid)) {
181
            $this->action = $this->request->get('action');
182
        }
183
184
        if (  empty($this->action) ) {
185
            $this->action = 'index';
186
        }
187
        if ( empty($this->groupId) ) {
188
            $this->addError('Bad request');
189
            $this->redirect('/');
190
        }
191
192
        $this->project     = $this->projectManager->getProject($this->groupId);
193
        $this->projectName = $this->project->getUnixName();
194
        if ( !$this->plugin_manager->isPluginAllowedForProject($this->plugin, $this->groupId) ) {
195
            $this->addError( $this->getText('project_service_not_available') );
196
            $this->redirect('/projects/'.$this->projectName.'/');
197
        }
198
199
        $this->permittedActions = array();
200
    }
201
202
    protected function instantiateView() {
203
        return new GitViews($this, new Git_GitRepositoryUrlManager($this->getPlugin()), $this->mirror_data_mapper, $this->permissions_manager);
204
    }
205
206
    private function routeGitSmartHTTP(Git_URL $url) {
207
        if (! $url->isSmartHTTP()) {
208
            return;
209
        }
210
211
        $repository = $url->getRepository();
212
        if (! $repository) {
213
            return;
214
        }
215
216
        $logger = new WrapperLogger($this->logger, 'http');
217
218
        $logger->debug('REQUEST_URI '.$_SERVER['REQUEST_URI']);
219
220
        $command_factory = new Git_HTTP_CommandFactory(
221
            $this->factory,
222
            new User_LoginManager(
223
                EventManager::instance(),
224
                UserManager::instance(),
225
                new User_PasswordExpirationChecker(),
226
                PasswordHandlerFactory::getPasswordHandler()
227
            ),
228
            PermissionsManager::instance(),
229
            new URLVerification(),
230
            $logger
231
        );
232
233
        $http_wrapper = new Git_HTTP_Wrapper($logger);
234
        $http_wrapper->stream($command_factory->getCommandForRepository($repository, $url));
0 ignored issues
show
It seems like $command_factory->getCom...tory($repository, $url) targeting Git_HTTP_CommandFactory::getCommandForRepository() can also be of type null; however, Git_HTTP_Wrapper::stream() does only seem to accept object<Git_HTTP_Command>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
235
        exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method routeGitSmartHTTP() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
236
    }
237
238
    private function routeUsingFriendlyURLs(Git_URL $url) {
239
        if (! $this->getPlugin()->areFriendlyUrlsActivated()) {
240
            return;
241
        }
242
243
        if (! $url->isFriendly()) {
244
            return;
245
        }
246
247
        $repository = $url->getRepository();
248
        if (! $repository) {
249
            return;
250
        }
251
252
        $this->request->set('action', 'view');
253
        $this->request->set('group_id', $repository->getProjectId());
254
        $this->request->set('repo_id', $repository->getId());
255
256
        $this->addUrlParametersToRequest($url);
257
    }
258
259
    private function addUrlParametersToRequest(Git_URL $url) {
260
        $url_parameters_as_string = $url->getParameters();
261
        if (! $url_parameters_as_string) {
262
            return;
263
        }
264
265
        parse_str($url_parameters_as_string, $_GET);
266
        parse_str($url_parameters_as_string, $_REQUEST);
267
268
        parse_str($url_parameters_as_string, $url_parameters);
269
        foreach ($url_parameters as $key => $value) {
0 ignored issues
show
The expression $url_parameters of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
270
            $this->request->set($key, $value);
271
        }
272
    }
273
274
    private function routeUsingStandardURLs(Git_URL $url) {
275
        if (! $url->isStandard()) {
276
            return;
277
        }
278
279
        $repository = $url->getRepository();
280
        if (! $repository) {
281
            $this->addError('Bad request');
282
            $this->redirect('/');
283
            return;
284
        }
285
286
        $project = $url->getProject();
287
        $this->redirectIfTryingToViewRepositoryAndUserFriendlyURLsActivated($project, $repository, $url->getParameters());
0 ignored issues
show
It seems like $project defined by $url->getProject() on line 286 can be null; however, Git::redirectIfTryingToV...FriendlyURLsActivated() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
288
289
        $this->request->set('group_id', $project->getId());
290
        $this->request->set('action', 'view');
291
        $this->request->set('repo_id', $repository->getId());
292
    }
293
294
    private function redirectIfTryingToViewRepositoryAndUserFriendlyURLsActivated(
295
        Project $project,
296
        GitRepository $repository,
297
        $parameters
298
    ) {
299
        if (! $this->getPlugin()->areFriendlyUrlsActivated()) {
300
            return;
301
        }
302
303
        $request_parameters = $parameters ? '?'.$parameters : '';
304
        $redirecting_url    = GIT_BASE_URL .'/'. $project->getUnixName() .'/'. $repository->getFullName() . $request_parameters;
305
306
        header("Location: $redirecting_url", TRUE, 301);
307
    }
308
309
    public function setPermissionsManager(GitPermissionsManager $permissions_manager) {
310
        $this->permissions_manager = $permissions_manager;
311
    }
312
313
    public function setProjectManager($projectManager) {
314
        $this->projectManager = $projectManager;
315
    }
316
317
    public function setFactory(GitRepositoryFactory $factory) {
318
        $this->factory = $factory;
319
    }
320
321
    public function setRequest(Codendi_Request $request) {
322
        $this->request = $request;
323
    }
324
325
    public function setUserManager(UserManager $userManager) {
326
        $this->userManager = $userManager;
327
    }
328
329
    public function setAction($action) {
330
        $this->action = $action;
331
    }
332
333
    public function setGroupId($groupId) {
334
        $this->groupId = $groupId;
335
    }
336
337
    public function setPermittedActions($permittedActions) {
338
        $this->permittedActions = $permittedActions;
339
    }
340
341
    protected function getText($key, $params = array()) {
342
        return $GLOBALS['Language']->getText('plugin_git', $key, $params);
343
    }
344
345
    /**
346
     * @return GitPlugin
347
     */
348
    public function getPlugin() {
349
        return $this->plugin;
350
    }
351
352
    protected function definePermittedActions($repoId, $user) {
353
        if ($this->permissions_manager->userIsGitAdmin($user, $this->projectManager->getProject($this->groupId))) {
354
            $this->permittedActions = array(
355
                'index',
356
                'view' ,
357
                'edit',
358
                'clone',
359
                'add',
360
                'del',
361
                'create',
362
                'confirm_deletion',
363
                'save',
364
                'repo_management',
365
                'mail',
366
                'fork',
367
                'set_private',
368
                'confirm_private',
369
                'fork_repositories',
370
                'admin',
371
                'admin-git-admins',
372
                'admin-gerrit-templates',
373
                'admin-default-settings',
374
                'fetch_git_config',
375
                'fetch_git_template',
376
                'fork_repositories_permissions',
377
                'do_fork_repositories',
378
                'view_last_git_pushes',
379
                'migrate_to_gerrit',
380
                'disconnect_gerrit',
381
                'delete_gerrit_project',
382
                'update_mirroring',
383
                'update_default_mirroring',
384
                'restore'
385
            );
386
            if ($this->areMirrorsEnabledForProject()) {
387
                $this->permittedActions[] = 'admin-mass-update';
388
            }
389
        } else {
390
            $this->addPermittedAction('index');
391
            $this->addPermittedAction('view_last_git_pushes');
392
            $this->addPermittedAction('fork_repositories');
393
            $this->addPermittedAction('fork_repositories_permissions');
394
            $this->addPermittedAction('do_fork_repositories');
395
396
            if ($repoId !== 0) {
397
                $repo = $this->factory->getRepositoryById($repoId);
398
                if ($repo && $repo->userCanRead($user)) {
399
                    $this->addPermittedAction('view');
400
                    $this->addPermittedAction('edit');
401
                    $this->addPermittedAction('clone');
402
                    if ($repo->belongsTo($user)) {
403
                        $this->addPermittedAction('repo_management');
404
                        $this->addPermittedAction('mail');
405
                        $this->addPermittedAction('del');
406
                        $this->addPermittedAction('confirm_deletion');
407
                        $this->addPermittedAction('save');
408
                    }
409
                }
410
            }
411
        }
412
    }
413
414
    public function request() {
415
        $valid = new Valid_String('repo_name');
416
        $valid->required();
417
        $repositoryName = null;
418
        if($this->request->valid($valid)) {
419
            $repositoryName = trim($this->request->get('repo_name'));
420
        }
421
        $valid = new Valid_UInt('repo_id');
422
        $valid->required();
423
        if($this->request->valid($valid)) {
424
            $repoId = $this->request->get('repo_id');
425
        } else {
426
            $repoId = 0;
427
        }
428
429
        $user = $this->userManager->getCurrentUser();
430
        //define access permissions
431
        $this->definePermittedActions($repoId, $user);
432
433
        //check permissions
434
        if ( empty($this->permittedActions) || !$this->isAPermittedAction($this->action) ) {
435
            $this->addError($this->getText('controller_access_denied'));
436
            $this->redirect('/plugins/git/?group_id='.$this->groupId);
437
            return;
438
        }
439
440
        $this->_informAboutPendingEvents($repoId);
441
        $this->_dispatchActionAndView($this->action, $repoId, $repositoryName, $user);
442
443
    }
444
445
    public function _dispatchActionAndView($action, $repoId, $repositoryName, $user) {
446
        $pane = $this->request->get('pane');
447
        switch ($action) {
448
            #CREATE REF
449
            case 'create':
450
                $this->addView('create');
451
                break;
452
            #admin
453
            case 'view':
454
                $this->addAction( 'getRepositoryDetails', array($this->groupId, $repoId) );
455
                $this->addView('view');
456
                break;
457
458
            #ADD REF
459
            case 'add':
460
                $this->addAction('createReference', array($this->groupId, $repositoryName) );
461
                $this->addView('index');
462
                break;
463
             #DELETE a repository
464
            case 'del':
465
                $this->addAction( 'deleteRepository', array($this->groupId, $repoId) );
466
                $this->addView('index');
467
                break;
468
            #EDIT
469
            case 'edit':
470
                $repository = $this->factory->getRepositoryById($repoId);
471
                if (empty($repository)) {
472
                    $this->addError($this->getText('actions_params_error'));
473
                    $this->redirect('/plugins/git/?action=index&group_id='. $this->groupId);
474
                    return false;
475
                }
476
                if ( $this->isAPermittedAction('clone') && $this->request->get('clone') ) {
477
                    $valid = new Valid_UInt('parent_id');
478
                    $valid->required();
479
                    if($this->request->valid($valid)) {
480
                        $parentId = (int)$this->request->get('parent_id');
481
                    }
482
                    $this->addAction( 'cloneRepository', array($this->groupId, $repositoryName, $parentId) );
483
                    $this->addAction( 'getRepositoryDetails', array($this->groupId, $parentId) );
484
                    $this->addView('view');
485
                } else if ( $this->isAPermittedAction('save') && $this->request->get('save') ) {
486
                    $repoDesc = null;
487
                    if ($this->request->exist('repo_desc')) {
488
                        $repoDesc = GitRepository::DEFAULT_DESCRIPTION;
489
                        $valid = new Valid_Text('repo_desc');
490
                        $valid->required();
491
                        if($this->request->valid($valid)) {
492
                            $repoDesc = $this->request->get('repo_desc');
493
                        }
494
                    }
495
                    $repoAccess = null;
496
                    $valid = new Valid_String('repo_access');
497
                    $valid->required();
498
                    if($this->request->valid($valid) || is_array($this->request->get('repo_access'))) {
499
                        $repoAccess = $this->request->get('repo_access');
500
                    }
501
                    $this->addAction('save', array($this->groupId, $repoId, $repoAccess, $repoDesc, $pane) );
502
                    $this->addView('view');
503
                } else {
504
                    $this->addError( $this->getText('controller_access_denied') );
505
                    $this->redirect('/plugins/git/?group_id='.$this->groupId);
506
                }
507
                break;
508
            #repo_management
509
            case 'repo_management':
510
                $repository = $this->factory->getRepositoryById($repoId);
511
                if (empty($repository)) {
512
                    $this->addError($this->getText('actions_repo_not_found'));
513
                    $this->redirect('/plugins/git/?action=index&group_id='. $this->groupId);
514
                    return false;
515
                }
516
                $this->addAction('repoManagement', array($repository));
517
                $this->addView('repoManagement');
518
                break;
519
            case 'mail':
520
                $this->processRepoManagementNotifications($pane, $repoId, $repositoryName, $user);
521
                break;
522
            #fork
523
            case 'fork':
524
                $this->addAction('repoManagement', array($this->groupId, $repoId));
525
                $this->addView('forkRepositories');
526
                break;
527
            #confirm_private
528
            case 'confirm_private':
529
                if ( $this->isAPermittedAction('confirm_deletion') && $this->request->get('confirm_deletion') ) {
530
                    $repository = $this->factory->getRepositoryById($repoId);
531
                    $this->addAction('confirmDeletion', array($this->groupId, $repository));
532
                    $this->addView('confirm_deletion', array( 0=>array('repo_id'=>$repoId) ) );
533
                }
534
                else if ( $this->isAPermittedAction('save') && $this->request->get('save') ) {
535
                    $valid = new Valid_Text('repo_desc');
536
                    $valid->required();
537
                    if($this->request->valid($valid)) {
538
                        $repoDesc = $this->request->get('repo_desc');
539
                    }
540
                    $valid = new Valid_String('repo_access');
541
                    $valid->required();
542
                    if($this->request->valid($valid)) {
543
                        $repoAccess = $this->request->get('repo_access');
544
                    }
545
                    $this->addAction('confirmPrivate', array($this->groupId, $repoId, $repoAccess, $repoDesc) );
546
                    $this->addView('confirmPrivate');
547
                }
548
                break;
549
             #SET TO PRIVATE
550
            case 'set_private':
551
                $this->addAction('setPrivate', array($this->groupId, $repoId));
552
                $this->addView('view');
553
                break;
554
            case 'fork_repositories':
555
                $this->addAction('getProjectRepositoryList', array($this->groupId));
556
                $this->addView('forkRepositories');
557
                break;
558
            case 'admin-git-admins':
559
                if ($this->request->get('submit')) {
560
                    $valid = new Valid_Numeric(GitPresenters_AdminGitAdminsPresenter::GIT_ADMIN_SELECTBOX_NAME);
561
                    $project = $this->projectManager->getProject($this->groupId);
562
563
                    if ($this->request->validArray($valid)) {
564
                        $select_project_ids = $this->request->get(GitPresenters_AdminGitAdminsPresenter::GIT_ADMIN_SELECTBOX_NAME);
565
566
                        if ($select_project_ids) {
567
                            $this->addAction('updateGitAdminGroups', array($project, $user, $select_project_ids));
568
                        } else {
569
                            $this->addError($this->getText('no_data_retrieved'));
570
                        }
571
                    } else {
572
                        $this->addError($this->getText('not_valid_request'));
573
                    }
574
                }
575
576
                $this->addView(
577
                    'adminGitAdminsView',
578
                    array($this->areMirrorsEnabledForProject())
579
                );
580
581
                break;
582
            case 'admin':
583
            case 'admin-gerrit-templates':
584
                $project = $this->projectManager->getProject($this->groupId);
585
586
                if ($this->request->get('save')) {
587
                    $template_content = $this->request->getValidated('git_admin_config_data','text');
588
                    if ($this->request->getValidated('git_admin_template_id','uint')) {
589
                        $template_id = $this->request->get('git_admin_template_id');
590
                        $this->addAction('updateTemplate', array($project, $user, $template_content, $template_id));
591
                    } else {
592
                        $template_name = $this->request->getValidated('git_admin_file_name','string');
593
                        $this->addAction('createTemplate', array($project, $user, $template_content, $template_name));
594
                    }
595
                }
596
597
                if ($this->request->get('delete')) {
598
                    if ($this->request->getValidated('git_admin_template_id','uint')) {
599
                        $template_id = $this->request->get('git_admin_template_id');
600
                        $this->addAction('deleteGerritTemplate', array($template_id, $project, $user));
601
                    }
602
                }
603
604
                if ($this->permissions_manager->userIsGitAdmin($user, $project)) {
605
                    $this->addAction('generateGerritRepositoryAndTemplateList', array($project, $user));
606
                    $this->addView(
607
                        'adminGerritTemplatesView',
608
                        array($this->areMirrorsEnabledForProject())
609
                    );
610
                } else {
611
                    $this->addError($this->getText('controller_access_denied'));
612
                    $this->redirect('/plugins/git/?action=index&group_id='. $this->groupId);
613
                    return false;
614
                }
615
616
                break;
617
            case 'admin-mass-update':
618
                if ($this->request->get('save-mass-change') || $this->request->get('go-to-mass-change')) {
619
                    $this->checkSynchronizerToken('/plugins/git/?group_id='. (int)$this->groupId .'&action=admin-mass-update');
620
621
                    $repositories = $this->getRepositoriesFromIds($this->request->get('repository_ids'));
622
623
                    if (! $repositories) {
624
                        $this->addError($this->getText('actions_repo_not_found'));
625
                        $this->redirect('/plugins/git/?action=index&group_id='. $this->groupId);
626
                    }
627
                }
628
629
                if ($this->request->get('go-to-mass-change')) {
630
                    $this->addAction('setSelectedRepositories', array($repositories));
631
                    $this->addView('adminMassUpdateView');
632
                    return;
633
                }
634
635
                if ($this->request->get('save-mass-change')) {
636
                    $this->addAction('updateMirroring', array(
637
                        $this->request->getProject(),
638
                        $repositories,
639
                        $this->request->get('selected_mirror_ids')
640
                    ));
641
                }
642
643
                $this->addView('adminMassUpdateSelectRepositoriesView');
644
645
                break;
646
            case 'admin-default-settings':
647
                $this->addView(
648
                    'adminDefaultSettings',
649
                    array($this->areMirrorsEnabledForProject())
650
                );
651
652
                break;
653
            case 'fetch_git_config':
654
                $project = $this->projectManager->getProject($this->groupId);
655
                $this->setDefaultPageRendering(false);
656
                $this->addAction('fetchGitConfig', array($repoId, $user, $project));
657
                break;
658
            case 'fetch_git_template':
659
                $project = $this->projectManager->getProject($this->groupId);
660
                $template_id = $this->request->getValidated('template_id','uint');
661
                $this->setDefaultPageRendering(false);
662
                $this->addAction('fetchGitTemplate', array($template_id, $user, $project));
663
                break;
664
            case 'fork_repositories_permissions':
665
                $scope = self::SCOPE_PERSONAL;
666
                $valid = new Valid_UInt('repos');
667
                $valid->required();
668
                if($this->request->validArray($valid)) {
669
                    $repos = $this->request->get('repos');
670
                }
671
                $valid = new Valid_UInt('to_project');
672
                if ($this->request->valid($valid)) {
673
                    $toProject = $this->request->get('to_project');
674
                }
675
                $valid = new Valid_String('path');
676
                $valid->required();
677
                $path = '';
678
                if($this->request->valid($valid)) {
679
                    $path = $this->request->get('path');
680
                }
681
                $valid = new Valid_String('choose_destination');
682
                $valid->required();
683
                if($this->request->valid($valid)) {
684
                    $scope = $this->request->get('choose_destination');
685
                }
686
                if (!empty($repos)) {
687
                    $this->addAction('forkRepositoriesPermissions', array($repos, $toProject, $path, $scope));
688
                    $this->addView('forkRepositoriesPermissions');
689
                } else {
690
                    $this->addError($this->getText('actions_params_error'));
691
                    $this->addAction('getProjectRepositoryList', array($this->groupId));
692
                    $this->addView('forkRepositories');
693
                }
694
                break;
695
            case 'do_fork_repositories':
696
                try {
697
                    if ($this->request->get('choose_destination') == self::SCOPE_PERSONAL) {
698
                        if ($this->user->isMember($this->groupId)) {
699
                            $this->_doDispatchForkRepositories($this->request, $user);
700
                        } else {
701
                            $this->addError($this->getText('controller_access_denied'));
702
                        }
703
                    } else {
704
                        $this->_doDispatchForkCrossProject($this->request, $user);
705
                    }
706
                } catch (MalformedPathException $e) {
707
                    $this->addError($this->getText('fork_malformed_path'));
708
                }
709
                $this->addAction('getProjectRepositoryList', array($this->groupId));
710
                $this->addView('forkRepositories');
711
                break;
712
            case "view_last_git_pushes":
713
                $vGroupId = new Valid_GroupId();
714
                $vGroupId->required();
715
                if ($this->request->valid($vGroupId)) {
716
                    $groupId = $this->request->get('group_id');
717
                }
718
                $vWeeksNumber = new Valid_UInt('weeks_number');
719
                if ($this->request->valid($vWeeksNumber)) {
720
                    $weeksNumber = $this->request->get('weeks_number');
721
                }
722
                if (empty($weeksNumber) || $weeksNumber > Git_LastPushesGraph::MAX_WEEKSNUMBER) {
723
                    $weeksNumber = 12;
724
                }
725
                $imageRenderer = new Git_LastPushesGraph($groupId, $weeksNumber);
726
                $imageRenderer->display();
727
                break;
728
            case 'migrate_to_gerrit':
729
                if (ForgeConfig::get('sys_auth_type') !== ForgeConfig::AUTH_TYPE_LDAP) {
730
                    $this->redirect('/plugins/git/?group_id='. $this->groupId);
731
                    break;
732
                }
733
734
                $repo                  = $this->factory->getRepositoryById($repoId);
735
                $remote_server_id      = $this->request->getValidated('remote_server_id', 'uint');
736
                $gerrit_template_id    = $this->getValidatedGerritTemplateId($repo);
737
738
                if (empty($repo) || empty($remote_server_id) || empty($gerrit_template_id)) {
739
                    $this->addError($this->getText('actions_params_error'));
740
                    $this->redirect('/plugins/git/?group_id='. $this->groupId);
741
                } else {
742
                    try {
743
                        $project_exists = $this->gerritProjectAlreadyExists($remote_server_id, $repo);
744
                        if ($project_exists) {
745
                            $this->addError($this->getText('gerrit_project_exists'));
746
                        } else {
747
                            $this->addAction('migrateToGerrit', array($repo, $remote_server_id, $gerrit_template_id));
748
                        }
749
                    } catch (Git_Driver_Gerrit_Exception $e) {
750
                        $this->addError($this->getText('gerrit_server_down'));
751
                    }
752
                    $this->addAction('redirectToRepoManagementWithMigrationAccessRightInformation', array($this->groupId, $repoId, $pane));
753
                }
754
                break;
755
            case 'disconnect_gerrit':
756
                $repo = $this->factory->getRepositoryById($repoId);
757
                if (empty($repo)) {
758
                    $this->addError($this->getText('actions_params_error'));
759
                    $this->redirect('/plugins/git/?group_id='. $this->groupId);
760
                } else {
761
                    $this->addAction('disconnectFromGerrit', array($repo));
762
                    $this->addAction('redirectToRepoManagement', array($this->groupId, $repoId, $pane));
763
                }
764
                break;
765
            case 'delete_gerrit_project':
766
                $repo                = $this->factory->getRepositoryById($repoId);
767
                $server              = $this->gerrit_server_factory->getServerById($repo->getRemoteServerId());
768
                $project_gerrit_name = $this->driver_factory->getDriver($server)->getGerritProjectName($repo);
0 ignored issues
show
It seems like $repo defined by $this->factory->getRepositoryById($repoId) on line 766 can be null; however, Git_Driver_GerritREST::getGerritProjectName() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
It seems like $repo defined by $this->factory->getRepositoryById($repoId) on line 766 can be null; however, Git_Driver_GerritLegacy::getGerritProjectName() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
769
770
                try {
771
                    $this->driver_factory->getDriver($server)->deleteProject($server, $project_gerrit_name);
772
                } catch (ProjectDeletionException $exception) {
773
                    $this->addError($this->getText(
774
                        'project_deletion_not_possible',
775
                        array(
776
                            $project_gerrit_name,
777
                            $exception->getMessage()
778
                        )
779
                    ));
780
                } catch (Git_Driver_Gerrit_Exception $e) {
781
                    $this->addError($this->getText('gerrit_server_down'));
782
                }
783
                $migrate_access_right = $this->request->existAndNonEmpty('migrate_access_right');
784
                $this->addAction('redirectToRepoManagementWithMigrationAccessRightInformation', array($this->groupId, $repoId, $pane));
785
                break;
786
787
            case 'update_mirroring':
788
                $repository = $this->factory->getRepositoryById($repoId);
789
                if (! $repository) {
790
                    $this->addError($this->getText('actions_repo_not_found'));
791
                }
792
793
                $selected_mirror_ids = $this->request->get('selected_mirror_ids');
794
795
                if (is_array($selected_mirror_ids)) {
796
                    $this->addAction('updateMirroring', array(
797
                        $this->request->getProject(),
798
                        array($repository),
799
                        $selected_mirror_ids
800
                    ));
801
                } else {
802
                    $this->addError($this->getText('actions_mirror_ids_not_valid'));
803
                }
804
805
                $this->addAction('redirectToRepoManagement', array($this->groupId, $repository->getId(), $pane));
806
                break;
807
808
            case 'update_default_mirroring':
809
                $project             = $this->request->getProject();
810
                $selected_mirror_ids = $this->request->get('selected_mirror_ids');
811
812
                if (is_array($selected_mirror_ids)) {
813
                    $this->addAction('updateDefaultMirroring', array($project, $selected_mirror_ids));
814
                } else {
815
                    $this->addError($this->getText('actions_mirror_ids_not_valid'));
816
                }
817
818
                $this->addView(
819
                    'adminDefaultSettings',
820
                    array($this->areMirrorsEnabledForProject())
821
                );
822
823
                break;
824
            case 'restore':
825
                $this->addAction('restoreRepository', array($repoId, $this->groupId));
826
                break;
827
828
            #LIST
829
            default:
830
831
                $user_id = null;
832
                $valid = new Valid_UInt('user');
833
                $valid->required();
834
                if($this->request->valid($valid)) {
835
                    $user_id = $this->request->get('user');
836
                    $this->addData(array('user' => $user_id));
837
                }
838
                $this->addAction( 'getProjectRepositoryList', array($this->groupId, $user_id) );
839
                $this->addView('index');
840
                break;
841
        }
842
    }
843
844
    private function getValidatedGerritTemplateId($repository) {
845
        if (empty($repository)) {
846
            return null;
847
        }
848
        $template_id = $this->request->getValidated('gerrit_template_id', 'string');
849
850
        if ($template_id && ($template_id == Git_Driver_Gerrit_ProjectCreator::NO_PERMISSIONS_MIGRATION || $template_id == Git_Driver_Gerrit_ProjectCreator::DEFAULT_PERMISSIONS_MIGRATION)) {
851
            return $template_id;
852
        }
853
854
        $template_id = $this->request->getValidated('gerrit_template_id', 'uint');
855
856
        if ($template_id) {
857
            try {
858
                $this->template_factory->getTemplate($template_id);
859
            } catch (Git_Template_NotFoundException $e) {
860
                return null;
861
            }
862
        }
863
864
        if ($this->project_creator->checkTemplateIsAvailableForProject($template_id, $repository)) {
865
            return $template_id;
866
        }
867
868
        return null;
869
    }
870
871
    private function gerritProjectAlreadyExists($remote_server_id, GitRepository $repo) {
872
        $gerrit_server       = $this->gerrit_server_factory->getServerById($remote_server_id);
873
        $driver              = $this->driver_factory->getDriver($gerrit_server);
874
        $gerrit_project_name = $driver->getGerritProjectName($repo);
875
876
        return $driver->doesTheProjectExist($gerrit_server, $gerrit_project_name);
877
    }
878
879
    private function processRepoManagementNotifications($pane, $repoId, $repositoryName, $user) {
880
        $this->addView('repoManagement');
881
        if ($this->request->exist('mail_prefix')) {
882
            $valid = new Valid_String('mail_prefix');
883
            $valid->required();
884
            $mailPrefix = $this->request->getValidated('mail_prefix', $valid, '');
885
            $this->addAction('notificationUpdatePrefix', array($this->groupId, $repoId, $mailPrefix, $pane));
886
        }
887
        $add_mail = $this->request->getValidated('add_mail');
888
        if ($add_mail) {
889
            $validMails = array();
890
            $mails      = array_map('trim', preg_split('/[,;]/', $add_mail));
891
            $rule       = new Rule_Email();
892
            $um         = UserManager::instance();
893
            foreach ($mails as $mail) {
894
                if ($rule->isValid($mail)) {
895
                    $validMails[] = $mail;
896
                } else {
897
                    $user = $um->findUser($mail);
898
                    if ($user) {
899
                        $mail = $user->getEmail();
900
                        if ($mail) {
901
                            $validMails[] = $mail;
902
                        } else {
903
                            $this->addError($this->getText('no_user_mail', array($mail)));
904
                        }
905
                    } else {
906
                        $this->addError($this->getText('no_user', array($mail)));
907
                    }
908
                }
909
            }
910
            $this->addAction('notificationAddMail', array($this->groupId, $repoId, $validMails, $pane));
911
        }
912
        $remove_mail = $this->request->get('remove_mail');
913
        if (is_array($remove_mail)) {
914
            $mails = array();
915
            $valid = new Valid_Email('remove_mail');
916
            $valid->required();
917
            if($this->request->validArray($valid)) {
918
                $mails = $this->request->get('remove_mail');
919
            }
920
            if (count($mails) > 0) {
921
                $this->addAction('notificationRemoveMail', array($this->groupId, $repoId, $mails, $pane));
922
            }
923
        }
924
        $this->addAction('redirectToRepoManagement', array($this->groupId, $repoId, $pane));
925
    }
926
927
    protected function _informAboutPendingEvents($repoId) {
928
        $sem = SystemEventManager::instance();
929
        $dar = $sem->_getDao()->searchWithParam('head', $this->groupId, array('GIT_REPO_CREATE', 'GIT_REPO_DELETE'), array(SystemEvent::STATUS_NEW, SystemEvent::STATUS_RUNNING));
930
        foreach ($dar as $row) {
931
            $p = explode(SystemEvent::PARAMETER_SEPARATOR, $row['parameters']);
932
            $repository = $this->factory->getDeletedRepository($p[1]);
933
            switch($row['type']) {
934
            case 'GIT_REPO_CREATE':
935
                $GLOBALS['Response']->addFeedback('info', $this->getText('feedback_event_create', array($p[1])));
936
                break;
937
938
            case 'GIT_REPO_DELETE':
939
                $GLOBALS['Response']->addFeedback('info', $this->getText('feedback_event_delete', array($repository->getFullName())));
940
                break;
941
            }
942
943
        }
944
945
        if ($repoId !== 0) {
946
            $dar = $sem->_getDao()->searchWithParam('head', $repoId, array('GIT_REPO_ACCESS'), array(SystemEvent::STATUS_NEW, SystemEvent::STATUS_RUNNING));
947
            foreach ($dar as $row) {
948
                $GLOBALS['Response']->addFeedback('info', $this->getText('feedback_event_access'));
949
            }
950
        }
951
    }
952
953
    /**
954
     * Instantiate an action based on a given name.
955
     *
956
     * Can be overriden to pass additionnal parameters to the action
957
     *
958
     * @param string $action The name of the action
959
     *
960
     * @return PluginActions
961
     */
962
    protected function instantiateAction($action) {
963
        return new $action(
964
            $this,
965
            $this->git_system_event_manager,
966
            $this->factory,
967
            $this->repository_manager,
968
            $this->gerrit_server_factory,
969
            $this->driver_factory,
970
            $this->gerrit_usermanager,
971
            $this->project_creator,
972
            $this->template_factory,
973
            $this->projectManager,
974
            $this->permissions_manager,
975
            $this->url_manager,
976
            $this->logger,
977
            $this->backend_gitolite,
978
            $this->mirror_data_mapper,
979
            new ProjectHistoryDao()
980
        );
981
    }
982
983
    public function _doDispatchForkCrossProject($request, $user) {
984
        $this->checkSynchronizerToken('/plugins/git/?group_id='. (int)$this->groupId .'&action=fork_repositories');
985
        $validators = array(new Valid_UInt('to_project'), new Valid_String('repos'), new Valid_Array('repo_access'));
986
987
        foreach ($validators as $validator) {
988
            $validator->required();
989
            if (!$request->valid($validator)) {
990
                $this->addError($this->getText('missing_parameter_'. $validator->key));
991
                $this->redirect('/plugins/git/?group_id='.$this->groupId);
992
                return;
993
            }
994
        }
995
        $to_project_id   = $request->get('to_project');
996
        if ($this->permissions_manager->userIsGitAdmin($user, $this->projectManager->getProject($to_project_id))){
997
            $to_project      = $this->projectManager->getProject($to_project_id);
998
            $repos_ids       = explode(',', $request->get('repos'));
999
            $repos           = $this->getRepositoriesFromIds($repos_ids);
1000
            $namespace       = '';
1001
            $scope           = GitRepository::REPO_SCOPE_PROJECT;
1002
            $redirect_url    = '/plugins/git/?group_id='. (int)$to_project_id;
1003
            $forkPermissions = $this->getForkPermissionsFromRequest($request);
1004
1005
            $this->addAction('fork', array($repos, $to_project, $namespace, $scope, $user, $GLOBALS['HTML'], $redirect_url, $forkPermissions));
1006
        } else {
1007
            $this->addError($this->getText('must_be_admin_to_create_project_repo'));
1008
        }
1009
    }
1010
1011
    protected function checkSynchronizerToken($url) {
1012
        $token = new CSRFSynchronizerToken($url);
1013
        $token->check();
1014
    }
1015
1016
    public function _doDispatchForkRepositories($request, $user) {
1017
        $this->addAction('getProjectRepositoryList', array($this->groupId));
1018
        $this->checkSynchronizerToken('/plugins/git/?group_id='. (int)$this->groupId .'&action=fork_repositories');
1019
1020
        $repos_ids = array();
1021
1022
        $valid = new Valid_String('path');
1023
        $valid->required();
1024
1025
        $path = '';
1026
        if($request->valid($valid)) {
1027
            $path = trim($request->get('path'));
1028
        }
1029
        $path = userRepoPath($user->getUserName(), $path);
1030
        $forkPermissions = $this->getForkPermissionsFromRequest($request);
1031
1032
        $valid = new Valid_String('repos');
1033
        $valid->required();
1034
        $repos_ids = explode(',', $request->get('repos'));
1035
        $to_project   = $this->projectManager->getProject($this->groupId);
1036
        $repos        = $this->getRepositoriesFromIds($repos_ids);
1037
        $scope        = GitRepository::REPO_SCOPE_INDIVIDUAL;
1038
        $redirect_url = '/plugins/git/?group_id='. (int)$this->groupId .'&user='. (int)$user->getId();
1039
        $this->addAction('fork', array($repos, $to_project, $path, $scope, $user, $GLOBALS['HTML'], $redirect_url, $forkPermissions));
1040
1041
    }
1042
1043
    /**
1044
     * @return array
1045
     */
1046
    private function getForkPermissionsFromRequest(Codendi_Request $request) {
1047
        $fork_permissions = $request->get('repo_access');
1048
        if ($fork_permissions) {
1049
            return $fork_permissions;
1050
        }
1051
        // when we fork a gerrit repository, the repo rights cannot
1052
        // be updated by the user on the intermediate screen and the
1053
        // repo_access is false. Forcing it to empty array to avoid
1054
        // fatal errors
1055
        return array();
1056
    }
1057
1058
    private function getRepositoriesFromIds($repository_ids) {
1059
        $repositories = array();
1060
1061
        foreach($repository_ids as $repository_id) {
1062
            $repository = $this->factory->getRepositoryById($repository_id);
1063
1064
            if (! $repository) {
1065
                return false;
1066
            }
1067
1068
            $repositories[] = $repository;
1069
        }
1070
1071
        return $repositories;
1072
    }
1073
1074
    /**
1075
     * Add pushes' logs stuff
1076
     *
1077
     * @param Array $params
1078
     *
1079
     * @return Void
1080
     */
1081
    public function logsDaily($params) {
1082
        $logger  = new GitLog();
1083
        $logger->logsDaily($params);
1084
    }
1085
1086
    private function areMirrorsEnabledForProject() {
1087
        return count($this->mirror_data_mapper->fetchAllForProject($this->project)) > 0;
1088
    }
1089
}
1090