Server::setHeaderContainer()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 6
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Stitcher\Application;
4
5
use GuzzleHttp\Psr7\Request;
6
use GuzzleHttp\Psr7\Response;
7
use GuzzleHttp\Psr7\ServerRequest;
8
use Pageon\Http\HeaderContainer;
9
use Stitcher\Exception\ErrorHandler;
10
use Stitcher\Exception\Http;
11
use Stitcher\Exception\StitcherException;
12
13
abstract class Server
14
{
15
    /** @var Router */
16
    protected $router;
17
18
    /** @var Request */
19
    protected $request;
20
21
    /** @var HeaderContainer */
22
    protected $headerContainer;
23
24
    /** @var \Stitcher\Exception\ErrorHandler */
25
    protected $errorHandler;
26
27
    abstract protected function handleStaticRoute(): ?Response;
28
29
    public function setRouter(Router $router): Server
30
    {
31
        $this->router = $router;
32
33
        return $this;
34
    }
35
36
    public function setHeaderContainer(HeaderContainer $headerContainer): Server
37
    {
38
        $this->headerContainer = $headerContainer;
39
40
        return $this;
41
    }
42
43
    public function setErrorHandler(ErrorHandler $errorHandler): Server
44
    {
45
        $this->errorHandler = $errorHandler;
46
47
        return $this;
48
    }
49
50
    public function run(): string
51
    {
52
        $response = $this->createResponse();
53
54
        return $this->handleResponse($response);
55
    }
56
57
    protected function getRequest(): Request
58
    {
59
        if (! $this->request) {
60
            $this->request = ServerRequest::fromGlobals();
0 ignored issues
show
Documentation Bug introduced by
It seems like \GuzzleHttp\Psr7\ServerRequest::fromGlobals() of type object<Psr\Http\Message\ServerRequestInterface> is incompatible with the declared type object<GuzzleHttp\Psr7\Request> of property $request.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
61
        }
62
63
        return $this->request;
64
    }
65
66
    protected function getCurrentPath(): string
67
    {
68
        $path = $this->getRequest()->getUri()->getPath();
69
70
        return $path === '' ? '/' : $path;
71
    }
72
73
    protected function handleDynamicRoute(): ?Response
74
    {
75
        if (! $this->router) {
76
            return null;
77
        }
78
79
        return $this->router->dispatch($this->getRequest());
80
    }
81
82
    protected function createResponse(): Response
83
    {
84
        if (
85
            $this->router
86
            && $redirectTo = $this->router->getRedirectForUrl($this->getCurrentPath())
87
        ) {
88
            return $this->createRedirectResponse($redirectTo);
89
        }
90
91
        try {
92
            $response = $this->handleStaticRoute();
93
94
            if (! $response) {
95
                $response = $this->handleDynamicRoute();
96
            }
97
        } catch (StitcherException $e) {
98
            $response = $this->createErrorResponse($e);
99
        }
100
101
        return $response ?? $this->createErrorResponse(
102
                Http::notFound(
103
                    $this->getCurrentPath()
104
                )
105
            );
106
    }
107
108
    protected function createRedirectResponse(string $targetUrl): Response
109
    {
110
        return new Response(301, ['Location' => $targetUrl]);
111
    }
112
113
    protected function createErrorResponse(StitcherException $exception): Response
114
    {
115
        $statusCode = $exception instanceof Http ? $exception->statusCode() : 500;
116
117
        $responseBody = $this->errorHandler->handle($statusCode, $exception);
118
119
        return new Response($statusCode, [], $responseBody);
120
    }
121
122
    protected function handleResponse(Response $response): string
123
    {
124
        foreach ($response->getHeaders() as $name => $headers) {
125
            header($name . ':'. implode(', ', $headers));
0 ignored issues
show
Security Response Splitting introduced by
$name . ':' . implode(', ', $headers) can contain request data and is used in response header context(s) leading to a potential security vulnerability.

4 paths for user data to reach this point

  1. Path: Read from $_SERVER, and $value is assigned in src/getallheaders.php on line 20
  1. Read from $_SERVER, and $value is assigned
    in vendor/src/getallheaders.php on line 20
  2. $headers is assigned
    in vendor/src/getallheaders.php on line 25
  3. getallheaders() returns tainted data, and $headers is assigned
    in vendor/src/ServerRequest.php on line 169
  4. $headers is passed to ServerRequest::__construct()
    in vendor/src/ServerRequest.php on line 174
  5. $headers is passed to Request::__construct()
    in vendor/src/ServerRequest.php on line 75
  6. $headers is passed to MessageTrait::setHeaders()
    in vendor/src/Request.php on line 45
  7. $value is assigned
    in vendor/src/MessageTrait.php on line 146
  8. Data is passed through array_map()
    in vendor/src/MessageTrait.php on line 181
  9. $value is assigned
    in vendor/src/MessageTrait.php on line 151
  10. $value is passed through array_merge(), and MessageTrait::$headers is assigned
    in vendor/src/MessageTrait.php on line 155
  11. Tainted property MessageTrait::$headers is read
    in vendor/src/MessageTrait.php on line 41
  12. MessageTrait::getHeaders() returns tainted data, and $name is assigned
    in src/Stitcher/Application/Server.php on line 124
  2. Path: Fetching key PHP_AUTH_USER from $_SERVER, and $_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass is escaped by base64_encode() for all (base64-encoded) context(s), and $headers is assigned in src/getallheaders.php on line 37
  1. Fetching key PHP_AUTH_USER from $_SERVER, and $_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass is escaped by base64_encode() for all (base64-encoded) context(s), and $headers is assigned
    in vendor/src/getallheaders.php on line 37
  2. getallheaders() returns tainted data, and $headers is assigned
    in vendor/src/ServerRequest.php on line 169
  3. $headers is passed to ServerRequest::__construct()
    in vendor/src/ServerRequest.php on line 174
  4. $headers is passed to Request::__construct()
    in vendor/src/ServerRequest.php on line 75
  5. $headers is passed to MessageTrait::setHeaders()
    in vendor/src/Request.php on line 45
  6. $value is assigned
    in vendor/src/MessageTrait.php on line 146
  7. Data is passed through array_map()
    in vendor/src/MessageTrait.php on line 181
  8. $value is assigned
    in vendor/src/MessageTrait.php on line 151
  9. $value is passed through array_merge(), and MessageTrait::$headers is assigned
    in vendor/src/MessageTrait.php on line 155
  10. Tainted property MessageTrait::$headers is read
    in vendor/src/MessageTrait.php on line 41
  11. MessageTrait::getHeaders() returns tainted data, and $name is assigned
    in src/Stitcher/Application/Server.php on line 124
  3. Path: Fetching key PHP_AUTH_PW from $_SERVER, and $basic_pass is assigned in src/getallheaders.php on line 36
  1. Fetching key PHP_AUTH_PW from $_SERVER, and $basic_pass is assigned
    in vendor/src/getallheaders.php on line 36
  2. $_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass is escaped by base64_encode() for all (base64-encoded) context(s), and $headers is assigned
    in vendor/src/getallheaders.php on line 37
  3. getallheaders() returns tainted data, and $headers is assigned
    in vendor/src/ServerRequest.php on line 169
  4. $headers is passed to ServerRequest::__construct()
    in vendor/src/ServerRequest.php on line 174
  5. $headers is passed to Request::__construct()
    in vendor/src/ServerRequest.php on line 75
  6. $headers is passed to MessageTrait::setHeaders()
    in vendor/src/Request.php on line 45
  7. $value is assigned
    in vendor/src/MessageTrait.php on line 146
  8. Data is passed through array_map()
    in vendor/src/MessageTrait.php on line 181
  9. $value is assigned
    in vendor/src/MessageTrait.php on line 151
  10. $value is passed through array_merge(), and MessageTrait::$headers is assigned
    in vendor/src/MessageTrait.php on line 155
  11. Tainted property MessageTrait::$headers is read
    in vendor/src/MessageTrait.php on line 41
  12. MessageTrait::getHeaders() returns tainted data, and $name is assigned
    in src/Stitcher/Application/Server.php on line 124
  4. Path: Fetching key PHP_AUTH_DIGEST from $_SERVER, and $headers is assigned in src/getallheaders.php on line 39
  1. Fetching key PHP_AUTH_DIGEST from $_SERVER, and $headers is assigned
    in vendor/src/getallheaders.php on line 39
  2. getallheaders() returns tainted data, and $headers is assigned
    in vendor/src/ServerRequest.php on line 169
  3. $headers is passed to ServerRequest::__construct()
    in vendor/src/ServerRequest.php on line 174
  4. $headers is passed to Request::__construct()
    in vendor/src/ServerRequest.php on line 75
  5. $headers is passed to MessageTrait::setHeaders()
    in vendor/src/Request.php on line 45
  6. $value is assigned
    in vendor/src/MessageTrait.php on line 146
  7. Data is passed through array_map()
    in vendor/src/MessageTrait.php on line 181
  8. $value is assigned
    in vendor/src/MessageTrait.php on line 151
  9. $value is passed through array_merge(), and MessageTrait::$headers is assigned
    in vendor/src/MessageTrait.php on line 155
  10. Tainted property MessageTrait::$headers is read
    in vendor/src/MessageTrait.php on line 41
  11. MessageTrait::getHeaders() returns tainted data, and $name is assigned
    in src/Stitcher/Application/Server.php on line 124

Response Splitting Attacks

Allowing an attacker to set a response header, opens your application to response splitting attacks; effectively allowing an attacker to send any response, he would like.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
126
        }
127
128
        if ($this->headerContainer) {
129
            foreach ($this->headerContainer as $header) {
130
                header((string) $header);
131
            }
132
        }
133
134
        http_response_code($response->getStatusCode());
135
136
        return $response->getBody()->getContents();
137
    }
138
}
139