Issues (273)

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.

controllers/MatchController.php (4 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
use Symfony\Component\Form\Form;
4
use Symfony\Component\Form\FormError;
5
use Symfony\Component\HttpFoundation\RedirectResponse;
6
use Symfony\Component\HttpFoundation\Request;
7
use Symfony\Component\HttpFoundation\StreamedResponse;
8
9
class MatchController extends CRUDController
10
{
11
    /**
12
     * Whether the last edited match has had its ELO changed, requiring an ELO
13
     * recalculation
14
     *
15
     * This is useful so that a confirmation form is shown, asking the user if
16
     * they want to recalculate ELOs
17
     *
18
     * @var bool
19
     */
20
    public $recalculateNeeded = false;
21
22
    public function listAction(Request $request, Player $me, Team $team = null, Player $player = null, $type = null)
23
    {
24
        /** @var MatchQueryBuilder $qb */
25
        $qb = self::getQueryBuilder();
26
        $currentPage = $this->getCurrentPage();
27
28
        if ($player) {
29
            $team = $player;
30
        }
31
32
        $query = $qb
33
            ->sortBy('time')->reverse()
34
            ->with($team, $type)
0 ignored issues
show
It seems like $team defined by parameter $team on line 22 can also be of type null; however, MatchQueryBuilder::with() does only seem to accept object<Team>|object<Player>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and 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...
35
            ->limit(50)->fromPage($currentPage)
36
        ;
37
38
        $matchType = $request->query->get('type', 'all');
39
40
        if (in_array($matchType, [Match::FUN, Match::OFFICIAL, Match::SPECIAL])) {
41
            $query->where('type')->is($matchType);
42
        }
43
44
        $matches = $query->getModels($fast = true);
45
46
        /** @var Match $match */
47
        foreach ($matches as $match) {
48
            // Don't show wrong labels for matches
49
            $match->getOriginalTimestamp()->setTimezone($me->getTimezone());
50
        }
51
52
        $matches = __::groupBy($matches, function ($match) {
53
            /** @var Match $match */
54
            $ts = $match->getOriginalTimestamp();
55
56 1
            if ($ts->year !== TimeDate::now()->year) {
57
                return $ts->format(TimeDate::DATE_FULL);
58 1
            }
59
60
            return $ts->format(TimeDate::DATE_MEDIUM);
61 1
        });
62
63
        return [
64 1
            'matchType'   => $type,
65 1
            'matches'     => $matches,
66 1
            'team'        => $team,
67 1
            'currentPage' => $currentPage,
68
            'totalPages'  => $qb->countPages(),
69
        ];
70
    }
71
72
    public function showAction(Match $match)
73
    {
74
        return array("match" => $match);
75 1
    }
76
77
    public function createAction(Player $me)
78
    {
79
        return $this->create($me, function (Match $match) use ($me) {
80 View Code Duplication
            if ($me->canEdit($match)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
81
                && $match->isOfficial()
82
                && (!$match->getTeamA()->isLastMatch($match)
83
                || !$match->getTeamB()->isLastMatch($match))
84
            ) {
85
                $url = Service::getGenerator()->generate('match_recalculate', array(
86
                    'match' => $match->getId(),
87
                ));
88
89
                return new RedirectResponse($url);
90
            }
91
        });
92
    }
93
94
    public function deleteAction(Player $me, Match $match)
95
    {
96
        return $this->delete($match, $me, function () use ($match, $me) {
97 View Code Duplication
            if ($match->getTeamA()->isLastMatch($match) && $match->getTeamB()->isLastMatch($match)) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
98
                $match->resetTeamElos();
99
                $match->resetPlayerElos();
100
            } elseif ($me->canEdit($match)) {
101
                $url = Service::getGenerator()->generate('match_recalculate', array(
102
                    'match' => $match->getId(),
103
                ));
104
105
                return new RedirectResponse($url);
106
            }
107
        });
108
    }
109
110 View Code Duplication
    public function editAction(Player $me, Match $match)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
111
    {
112
        // TODO: Generating this response is unnecessary
113
        $response = $this->edit($match, $me, "match");
114
115
        if ($this->recalculateNeeded && $match->isOfficial()) {
116
            // Redirect to a confirmation form if we are assigning a new leader
117
            $url = Service::getGenerator()->generate('match_recalculate', array(
118
                'match' => $match->getId(),
119
            ));
120
121
            return new RedirectResponse($url);
122
        }
123
124
        return $response;
125
    }
126
127
    public function recalculateAction(Player $me, $match)
128
    {
129
        $match = Match::get($match); // get a match even if it's deleted
130
131
        if (!$me->canEdit($match)) {
132
            throw new ForbiddenException("You are not allowed to edit that match.");
133
        }
134
135
        if (!$match->isOfficial()) {
136
            throw new BadRequestException("You can't recalculate ELO history for a special match.");
137
        }
138
139
        return $this->showConfirmationForm(function () use ($match) {
140
            $response = new StreamedResponse();
141
            $response->headers->set('Content-Type', 'text/plain');
142
            $response->setCallback(function () use ($match) {
143
                $this->log(Match::recalculateMatchesSince($match, function ($event) {
144
                    switch ($event['type']) {
145
                        case 'recalculation.count':
146
                            echo $event['type'] . "\n";
147
                            break;
148
149
                        case 'recalculation.progress':
150
                            echo 'm';
151
                            break;
152
153
                        case 'recalculation.complete':
154
                            echo "\n\nCalculation successful\n";
155
                            break;
156
157
                        default:
158
                            break;
159
                    }
160
                }));
161
            });
162 1
            $response->send();
163
        }, "Do you want to recalculate ELO history for all teams and matches after the specified match?",
164 1
            "ELO history recalculated",
165
            "Recalculate ELOs",
166
            function () use ($match) {
167 1
                if ($match->isDeleted()) {
168 1
                    return new RedirectResponse($match->getURL('list'));
169 1
                }
170 1
171
                return new RedirectResponse($match->getURL('show'));
172
            },
173
            "Match/recalculate.html.twig",
174
            $noButton = true
175 1
        );
176
    }
177
178 1
    /**
179
     * Echo a string and flush the buffers
180
     *
181
     * Useful for streamed AJAX responses
182 1
     *
183 1
     * @param string $string The string to echo
184
     */
185 1
    private function log($string)
186
    {
187
        echo $string;
188
        ob_flush();
189 1
        flush();
190 1
    }
191 1
192
    /**
193
     * {@inheritdoc}
194 1
     */
195
    protected function getMessages($type, $name = '')
196 1
    {
197 1
        $messages = parent::getMessages($type, $name);
198 1
199 1
        // Don't show the match info on the successful create/edit message
200
        foreach ($messages as &$action) {
201 1
            foreach ($action as &$status) {
202 1
                if (isset($status['named'])) {
203
                    $status['named'] = $status['unnamed'];
204 1
                }
205
            }
206 1
        }
207 1
208 1
        return $messages;
209
    }
210 1
211 1
    /**
212 1
     * @param Form $form
213
     */
214
    protected function validate($form)
215
    {
216
        // Make sure that two different teams participated in a match, i.e. a team
217 1
        // didn't match against itself
218
        $firstTeam  = $form->get('first_team')->get('team')->getData();
219
        $secondTeam = $form->get('second_team')->get('team')->getData();
220
221
        if (!$firstTeam || !$secondTeam) {
222
            return;
223
        }
224
225
        if ($firstTeam->isSameAs($secondTeam)) {
226
            $message = "You can't report a match where a team played against itself!";
227
            $form->addError(new FormError($message));
228
        }
229
230
        $matchType = $form->get('type')->getData();
231
232
        foreach (array('first_team', 'second_team') as $team) {
233
            $input = $form->get($team);
234
            $teamInput = $input->get('team');
235
            $teamParticipants = $input->get('participants');
236
237
            if ($matchType === Match::FUN) {
238
                if (!$teamInput->getData() instanceof ColorTeam) {
239
                    $message = "Please enter a team color for fun and special matches.";
240
                    $teamInput->addError(new FormError($message));
241
                }
242
            } elseif ($matchType === Match::OFFICIAL) {
243
                if ($teamInput->getData() instanceof ColorTeam) {
244
                    $participants = $teamParticipants->getData();
245
246
                    if (empty($participants)) {
247
                        $message = 'A player roster is necessary for a color team for a mixed official match.';
248
                        $teamInput->addError(new FormError($message));
249
                    }
250
                }
251
            }
252
        }
253
    }
254
255
    /**
256
     * @param Form $form
257
     * @param Match $match
258
     */
259
    protected function validateEdit($form, $match)
260
    {
261
        if ($match->isOfficial() && $form->get('type')->getData() !== Match::OFFICIAL) {
262
            $message = "You cannot change this match's type.";
263
            $form->get('type')->addError(new FormError($message));
264
        } elseif (!$match->isOfficial() && $form->get('type')->getData() === Match::OFFICIAL) {
265
            $message = "You can't make this an official match.";
266
            $form->get('type')->addError(new FormError($message));
267
        }
268
    }
269
}
270