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 (5)

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/ServerRequest.php (1 issue)

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
declare(strict_types=1);
4
5
namespace Patoui\Router;
6
7
use InvalidArgumentException;
8
use Psr\Http\Message\ServerRequestInterface;
9
use Psr\Http\Message\StreamInterface;
10
use Psr\Http\Message\UploadedFileInterface;
11
use Psr\Http\Message\UriInterface;
12
use RuntimeException;
13
14
final class ServerRequest implements ServerRequestInterface
15
{
16
    /**
17
     * @var string Represent the HTTP version number (e.g., "1.1", "1.0")
18
     */
19
    private string $version;
0 ignored issues
show
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_STRING, expecting T_FUNCTION or T_CONST
Loading history...
20
21
    /**
22
     * @var array<array> Contains header by key and array.
23
     * e.g. ['content-type' => ['application/json']]
24
     */
25
    private array $headers;
26
27
    /** @var StreamInterface */
28
    private StreamInterface $body;
29
30
    /** @var string */
31
    private string $requestTarget;
32
33
    /** @var string */
34
    private string $method;
35
36
    /** @var UriInterface */
37
    private UriInterface $uri;
38
39
    /** @var array<mixed> */
40
    private array $serverParams;
41
42
    /** @var array<mixed> */
43
    private array $cookieParams;
44
45
    /** @var array<mixed> */
46
    private array $queryParams;
47
48
    /** @var array<UploadedFileInterface> */
49
    private array $uploadedFiles;
50
51
    /** @var null|array<mixed>|object */
52
    private $parsedBody;
53
54
    /** @var array<mixed> */
55
    private array $attributes;
56
57
    /**
58
     * ServerRequest constructor.
59
     * @param string                       $version
60
     * @param array<array>                 $headers
61
     * @param StreamInterface              $body
62
     * @param string                       $requestTarget
63
     * @param string                       $method
64
     * @param UriInterface                 $uri
65
     * @param array<mixed>                 $serverParams
66
     * @param array<mixed>                 $cookieParams
67
     * @param array<string>                $queryParams
68
     * @param array<UploadedFileInterface> $uploadedFiles
69
     */
70
    public function __construct(
71
        string $version,
72
        array $headers,
73
        StreamInterface $body,
74
        string $requestTarget,
75
        string $method,
76
        UriInterface $uri,
77
        array $serverParams,
78
        array $cookieParams,
79
        array $queryParams,
80
        array $uploadedFiles
81
    ) {
82
        $this->validateProtocolVersion($version);
83
        $this->validateHeaders($headers);
84
        $this->validateMethod($method);
85
        $this->validateUploadedFiles($uploadedFiles);
86
87
        $this->version = $version;
88
        $this->headers = $headers;
89
        $this->body = $body;
90
        $this->requestTarget = $requestTarget;
91
        $this->method = $method;
92
        $this->uri = $uri;
93
        $this->serverParams = $serverParams;
94
        $this->cookieParams = $cookieParams;
95
        $this->queryParams = $queryParams;
96
        /** @psalm-suppress MixedPropertyTypeCoercion */
97
        $this->uploadedFiles = $uploadedFiles;
98
        $this->attributes = [];
99
    }
100
101
    /**
102
     * Create instance of server request based on global values.
103
     * @return static
104
     */
105
    public static function makeWithGlobals(): self
106
    {
107
        $protocolVersion = (string) ($_SERVER['SERVER_PROTOCOL'] ?? '1.1');
108
        $protocolVersion = str_replace('HTTP/', '', $protocolVersion);
109
        $requestTarget = (string) ($_SERVER['REQUEST_URI'] ?? '/');
110
        $method = (string) ($_SERVER['REQUEST_METHOD'] ?? 'GET');
111
        $queryParameters = array_filter($_GET, static function ($queryParameter) {
112
            return is_string($queryParameter);
113
        });
114
        $resource = fopen('php://memory', 'rb+');
115
116
        if ($resource === false) {
117
            throw new RuntimeException('Unabled to open memory resource');
118
        }
119
120
        // TODO: identify potential risk of using globals
121
        return new static(
122
            $protocolVersion,
123
            Headers::getHeadersArrayFromGlobals(),
124
            new Stream($resource),
125
            $requestTarget,
126
            $method,
127
            new Uri($requestTarget),
128
            $_SERVER,
129
            $_COOKIE,
130
            $queryParameters,
131
            UploadedFile::makeWithGlobals()
132
        );
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138
    public function getProtocolVersion()
139
    {
140
        return $this->version;
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146
    public function withProtocolVersion($version): self
147
    {
148
        $this->validateProtocolVersion($version);
149
150
        $instance = clone $this;
151
        $instance->version = $version;
152
153
        return $instance;
154
    }
155
156
    /**
157
     * Verifies the protocol version.
158
     *
159
     * @throws InvalidArgumentException
160
     * @param  string  $version The version string MUST contain only the HTTP
161
     * version number (e.g., "1.1", "1.0").
162
     */
163
    private function validateProtocolVersion(string $version): void
164
    {
165
        if (! in_array($version, ['1.1', '2.0'])) {
166
            throw new InvalidArgumentException("Invalid HTTP version: {$version}");
167
        }
168
    }
169
170
    /**
171
     * @psalm-suppress MixedReturnTypeCoercion
172
     * @see ServerRequestInterface::getHeaders()
173
     * @return array<array-key, array<array-key, string>>
174
     */
175
    public function getHeaders(): array
176
    {
177
        return $this->headers;
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183
    public function hasHeader($name)
184
    {
185
        return array_key_exists(
186
            str_replace('-', '_', mb_strtoupper($name)),
187
            array_change_key_case($this->headers, CASE_UPPER)
188
        );
189
    }
190
191
    /**
192
     * @psalm-suppress MixedReturnTypeCoercion
193
     * {@inheritdoc}
194
     */
195
    public function getHeader($name)
196
    {
197
        $name = str_replace('-', '_', mb_strtoupper($name));
198
        $headers = array_change_key_case($this->headers, CASE_UPPER);
199
200
        if (array_key_exists($name, $headers) === false) {
201
            return [];
202
        }
203
204
        return $headers[$name];
205
    }
206
207
    /**
208
     * {@inheritdoc}
209
     */
210
    public function getHeaderLine($name)
211
    {
212
        return implode(',', $this->getHeader($name));
213
    }
214
215
    /**
216
     * {@inheritdoc}
217
     */
218
    public function withHeader($name, $value)
219
    {
220
        $newHeaders = array_merge($this->getHeaders(), [$name => [$value]]);
221
222
        $instance = clone $this;
223
        /** @psalm-suppress MixedPropertyTypeCoercion */
224
        $instance->headers = $newHeaders;
225
226
        return $instance;
227
    }
228
229
    /**
230
     * {@inheritdoc}
231
     */
232
    public function withAddedHeader($name, $value)
233
    {
234
        $newHeaders = $this->getHeaders();
235
        $headerToUpdate = $this->getHeader($name);
236
        $headerToUpdate[] = $value;
237
        $newHeaders[$name] = $headerToUpdate;
238
239
        $instance = clone $this;
240
        /** @psalm-suppress MixedPropertyTypeCoercion */
241
        $instance->headers = $newHeaders;
242
243
        return $instance;
244
    }
245
246
    /**
247
     * {@inheritdoc}
248
     */
249
    public function withoutHeader($name)
250
    {
251
        $newHeaders = $this->getHeaders();
252
        unset($newHeaders[$name]);
253
254
        $instance = clone $this;
255
        /** @psalm-suppress MixedPropertyTypeCoercion */
256
        $instance->headers = $newHeaders;
257
258
        return $instance;
259
    }
260
261
    /**
262
     * Verifies the headers are valid.
263
     *
264
     * @throws InvalidArgumentException
265
     * @param  array<array> $headers Headers for the incoming request
266
     */
267
    private function validateHeaders(array $headers): void
268
    {
269
        $exceptionMessage = 'Invalid headers: '.json_encode($headers);
270
271
        if (empty($headers)) {
272
            return;
273
        }
274
275
        /** @psalm-suppress RedundantConditionGivenDocblockType */
276
        $headersWithArraysOnly = array_filter($headers, static function ($header) {
277
            return is_array($header);
278
        });
279
280
        if (count($headers) !== count($headersWithArraysOnly)) {
281
            throw new InvalidArgumentException($exceptionMessage);
282
        }
283
284
        foreach ($headers as $key => $header) {
285
            $headerWithStringValuesOnly = array_filter($header, static function ($headerValue) {
286
                return is_string($headerValue);
287
            });
288
289
            if (count($header) !== count($headerWithStringValuesOnly)) {
290
                throw new InvalidArgumentException($exceptionMessage);
291
            }
292
        }
293
    }
294
295
    /**
296
     * {@inheritdoc}
297
     */
298
    public function getBody()
299
    {
300
        return $this->body;
301
    }
302
303
    /**
304
     * {@inheritdoc}
305
     */
306
    public function withBody(StreamInterface $body)
307
    {
308
        $instance = clone $this;
309
        $instance->body = $body;
310
311
        return $instance;
312
    }
313
314
    /**
315
     * {@inheritdoc}
316
     */
317
    public function getRequestTarget()
318
    {
319
        return $this->requestTarget;
320
    }
321
322
    /**
323
     * {@inheritdoc}
324
     */
325
    public function withRequestTarget($requestTarget)
326
    {
327
        $instance = clone $this;
328
        $instance->requestTarget = (string) $requestTarget;
329
330
        return $instance;
331
    }
332
333
    /**
334
     * Verifies the HTTP method is valid.
335
     *
336
     * @throws InvalidArgumentException
337
     * @param  string  $method HTTP method for the incoming request
338
     */
339
    private function validateMethod(string $method): void
340
    {
341
        if (! in_array(strtoupper($method), ['POST', 'GET', 'OPTIONS'])) {
342
            throw new InvalidArgumentException("Invalid HTTP method: {$method}");
343
        }
344
    }
345
346
    /**
347
     * {@inheritdoc}
348
     */
349
    public function getMethod()
350
    {
351
        return $this->method;
352
    }
353
354
    /**
355
     * {@inheritdoc}
356
     */
357
    public function withMethod($method)
358
    {
359
        $this->validateMethod($method);
360
361
        $instance = clone $this;
362
        $instance->method = $method;
363
364
        return $instance;
365
    }
366
367
    /**
368
     * {@inheritdoc}
369
     */
370
    public function getUri()
371
    {
372
        return $this->uri;
373
    }
374
375
    /**
376
     * {@inheritdoc}
377
     */
378
    public function withUri(UriInterface $uri, $preserveHost = false)
379
    {
380
        $headers = $this->getHeaders();
381
        $currentUriHost = $this->uri->getHost();
382
383
        if ($preserveHost && $currentUriHost) {
384
            $headers['HTTP_HOST'] = [$currentUriHost];
385
        }
386
387
        $instance = clone $this;
388
        /** @psalm-suppress MixedPropertyTypeCoercion */
389
        $instance->headers = $headers;
390
        $instance->uri = $uri;
391
392
        return $instance;
393
    }
394
395
    /**
396
     * @return array<mixed>
397
     */
398
    public function getServerParams()
399
    {
400
        return $this->serverParams;
401
    }
402
403
    /**
404
     * @return array<mixed>
405
     * @see ServerRequestInterface::getCookieParams()
406
     */
407
    public function getCookieParams()
408
    {
409
        return $this->cookieParams;
410
    }
411
412
    /**
413
     * @param array<mixed> $cookies Array of key/value pairs representing cookies.
414
     * @return static
415
     * @see ServerRequestInterface::withCookieParams()
416
     */
417
    public function withCookieParams(array $cookies)
418
    {
419
        $instance = clone $this;
420
        $instance->cookieParams = $cookies;
421
422
        return $instance;
423
    }
424
425
    /**
426
     * @return array<mixed>
427
     * @see ServerRequestInterface::getQueryParams()
428
     */
429
    public function getQueryParams()
430
    {
431
        return $this->queryParams;
432
    }
433
434
    /**
435
     * @param array<mixed> $query Array of query string arguments, typically from
436
     *     $_GET.
437
     * @return static
438
     * @see ServerRequestInterface::withQueryParams()
439
     */
440
    public function withQueryParams(array $query)
441
    {
442
        $instance = clone $this;
443
        $instance->queryParams = array_merge($this->getQueryParams(), $query);
444
445
        return $instance;
446
    }
447
448
    /**
449
     * @return array<UploadedFileInterface> An array tree of UploadedFileInterface instances;
450
     * an empty array MUST be returned if no data is present.
451
     *@see ServerRequestInterface::getUploadedFiles()
452
     */
453
    public function getUploadedFiles()
454
    {
455
        return $this->uploadedFiles;
456
    }
457
458
    /**
459
     * @psalm-suppress MoreSpecificImplementedParamType
460
     * @param array<UploadedFileInterface> $uploadedFiles An array tree of UploadedFileInterface instances.
461
     * @return static
462
     * @throws \InvalidArgumentException if an invalid structure is provided.
463
     * @see ServerRequestInterface::withUploadedFiles()
464
     */
465
    public function withUploadedFiles(array $uploadedFiles)
466
    {
467
        $uploadedFiles = $this->validateUploadedFiles($uploadedFiles);
468
469
        $instance = clone $this;
470
        /** @psalm-suppress MixedPropertyTypeCoercion */
471
        $instance->uploadedFiles = $uploadedFiles;
472
473
        return $instance;
474
    }
475
476
    /**
477
     * @param array<UploadedFileInterface> $uploadedFiles
478
     * @return array<UploadedFileInterface>
479
     */
480
    private function validateUploadedFiles(array $uploadedFiles): array
481
    {
482
        /** @psalm-suppress RedundantConditionGivenDocblockType */
483
        $filteredUploadedFiles = array_filter($uploadedFiles, function ($uploadedFile) {
484
            return $uploadedFile instanceof UploadedFileInterface;
485
        });
486
487
        if (count($filteredUploadedFiles) !== count($uploadedFiles)) {
488
            throw new InvalidArgumentException(
489
                'Must be an array with instances of '
490
                .UploadedFileInterface::class
491
            );
492
        }
493
494
        return $filteredUploadedFiles;
495
    }
496
497
    /**
498
     * @return null|array<mixed>|object $data
499
     * @see ServerRequestInterface::getParsedBody()
500
     */
501
    public function getParsedBody()
502
    {
503
        $isPost = $this->isPostRequest();
504
505
        if ($isPost) {
506
            return $_POST;
507
        }
508
509
        return $this->parsedBody;
510
    }
511
512
    /**
513
     * @param null|array<mixed>|object $data
514
     * @return static
515
     * @see ServerRequestInterface::withParsedBody()
516
     */
517
    public function withParsedBody($data)
518
    {
519
        /** @psalm-suppress DocblockTypeContradiction */
520
        if ($data !== null && ! is_object($data) && ! is_array($data)) {
521
            throw new InvalidArgumentException(
522
                'Parsed body must be of type: null, array, or object'
523
            );
524
        }
525
526
        $data = (array) $data;
527
        $isPost = $this->isPostRequest();
528
        $instance = clone $this;
529
        $instance->parsedBody = array_merge((array) $this->getParsedBody(), $data);
530
531
        if ($isPost) {
532
            // TODO: identify potential risk with assigning values to the super global variable
533
            $_POST = array_merge($_POST, $data);
534
        }
535
536
        return $instance;
537
    }
538
539
    /**
540
     * @return array<mixed>
541
     * @see ServerRequestInterface::getAttributes()
542
     */
543
    public function getAttributes()
544
    {
545
        return $this->attributes;
546
    }
547
548
    /**
549
     * {@inheritdoc}
550
     */
551
    public function getAttribute($name, $default = null)
552
    {
553
        return $this->attributes[$name] ?? $default;
554
    }
555
556
    /**
557
     * {@inheritdoc}
558
     */
559
    public function withAttribute($name, $value)
560
    {
561
        $instance = clone $this;
562
        $instance->attributes[$name] = $value;
563
564
        return $instance;
565
    }
566
567
    /**
568
     * {@inheritdoc}
569
     */
570
    public function withoutAttribute($name)
571
    {
572
        $instance = clone $this;
573
574
        unset($instance->attributes[$name]);
575
576
        return $instance;
577
    }
578
579
    /**
580
     * Determines if the request is a POST request based on content type headers.
581
     * @return bool
582
     */
583
    private function isPostRequest(): bool
584
    {
585
        foreach ($this->getHeader('content-type') as $contentType) {
586
            if (stripos($contentType, 'application/x-www-form-urlencoded') === 0 ||
587
                stripos($contentType, 'multipart/form-data') === 0) {
588
                return true;
589
            }
590
        }
591
592
        return false;
593
    }
594
}
595