Issues (3)

Security Analysis    no request data  

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/StaticWebServer.php (3 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
namespace Jalle19\ReactHttpStatic;
4
5
use Dflydev\ApacheMimeTypes\Parser as ContentTypeParser;
6
use Dflydev\ApacheMimeTypes\PhpRepository;
7
use Jalle19\ReactHttpStatic\Authentication\Handler\HandlerInterface;
8
use Psr\Log\LoggerInterface;
9
use React\Http\Server;
10
use React\Http\Request;
11
use React\Http\Response;
12
13
/**
14
 * Class StaticWebServer
15
 * @package   Jalle19\ReactHttpStatic
16
 * @copyright Copyright &copy; Sam Stenvall 2016-
17
 * @license   @license https://opensource.org/licenses/MIT
18
 */
19
class StaticWebServer
20
{
21
22
    /**
23
     * @var Server
24
     */
25
    private $httpServer;
26
27
    /**
28
     * @var string the absolute path to the directory where files are served from
29
     */
30
    private $webroot;
31
32
    /**
33
     * @var HandlerInterface
34
     */
35
    private $authenticationHandler;
36
37
    /**
38
     * @var LoggerInterface
39
     */
40
    private $logger;
41
42
    /**
43
     * @var ContentTypeParser
44
     */
45
    private $contentTypeParser;
46
47
    /**
48
     * @var array
49
     */
50
    private $indexFiles = [
51
        'index.htm',
52
        'index.html',
53
    ];
54
55
56
    /**
57
     * StaticWebServer constructor.
58
     *
59
     * @param Server               $httpServer
60
     * @param string               $webroot
61
     * @param LoggerInterface|null $logger
62
     */
63
    public function __construct(Server $httpServer, $webroot, LoggerInterface $logger = null)
64
    {
65
        if (!file_exists($webroot)) {
66
            throw new \InvalidArgumentException('The specified webroot path does not exist');
67
        }
68
69
        $this->httpServer = $httpServer;
70
        $this->webroot    = $webroot;
71
        $this->logger     = $logger;
72
73
        // Attach the request handler
74
        $this->httpServer->on('request', [$this, 'handleRequest']);
75
76
        // Configure the content type parser
77
        $this->contentTypeParser = new ContentTypeParser();
78
    }
79
80
81
    /**
82
     * @return string
83
     */
84
    public function getWebroot()
85
    {
86
        return $this->webroot;
87
    }
88
89
90
    /**
91
     * @param string $webroot
92
     *
93
     * @return StaticWebServer
94
     */
95
    public function setWebroot($webroot)
96
    {
97
        $this->webroot = $webroot;
98
99
        return $this;
100
    }
101
102
103
    /**
104
     * @return LoggerInterface
105
     */
106
    public function getLogger()
107
    {
108
        return $this->logger;
109
    }
110
111
112
    /**
113
     * @param LoggerInterface $logger
114
     *
115
     * @return StaticWebServer
116
     */
117
    public function setLogger($logger)
118
    {
119
        $this->logger = $logger;
120
121
        return $this;
122
    }
123
124
125
    /**
126
     * @return array
127
     */
128
    public function getIndexFiles()
129
    {
130
        return $this->indexFiles;
131
    }
132
133
134
    /**
135
     * @param array $indexFiles
136
     *
137
     * @return StaticWebServer
138
     */
139
    public function setIndexFiles($indexFiles)
140
    {
141
        $this->indexFiles = $indexFiles;
142
143
        return $this;
144
    }
145
146
147
    /**
148
     * @param HandlerInterface|null $authenticationHandler
149
     *
150
     * @return StaticWebServer
151
     */
152
    public function setAuthenticationHandler($authenticationHandler)
153
    {
154
        $this->authenticationHandler = $authenticationHandler;
155
156
        return $this;
157
    }
158
159
160
    /**
161
     * @param Request  $request
162
     * @param Response $response
163
     */
164
    public function handleRequest(Request $request, Response $response)
165
    {
166
        $requestPath = $request->getPath();
167
        $filePath    = $this->resolvePath($requestPath);
168
169
        if ($this->logger !== null) {
170
            $this->logger->debug('Got HTTP request (request path: {requestPath}, resolved path: {resolvedPath})', [
171
                'requestPath'  => $requestPath,
172
                'resolvedPath' => $filePath,
173
            ]);
174
        }
175
176
        if ($this->authenticationHandler instanceof HandlerInterface) {
177
            if (!$this->authenticationHandler->handle($request)) {
178
                if ($this->logger !== null) {
179
                    $this->logger->warning('Client failed authentication');
180
                }
181
182
                $this->authenticationHandler->requireAuthentication($response);
183
184
                return;
185
            }
186
        }
187
188
        if (file_exists($filePath)) {
189
            if (is_readable($filePath)) {
190
                $response->writeHead(200, [
191
                    'Content-Type' => $this->getContentType($filePath),
0 ignored issues
show
It seems like $filePath defined by $this->resolvePath($requestPath) on line 167 can also be of type false; however, Jalle19\ReactHttpStatic\...erver::getContentType() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
192
                ]);
193
194
                $response->end(file_get_contents($filePath));
195 View Code Duplication
            } else {
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...
196
                if ($this->logger !== null) {
197
                    $this->logger->error('HTTP request failed, file unreadable ({filePath})', [
198
                        'filePath' => $filePath,
199
                    ]);    
200
                }
201
                
202
                $response->writeHead(403, ['Content-Type' => 'text/plain']);
203
                $response->end("Forbidden\n");
204
            }
205 View Code Duplication
        } else {
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...
206
            if ($this->logger !== null) {
207
                $this->logger->error('HTTP request failed, file not found ({filePath})', [
208
                    'filePath' => $filePath,
209
                ]);    
210
            }
211
            
212
            $response->writeHead(404, ['Content-Type' => 'text/plain']);
213
            $response->end("Not found\n");
214
        }
215
    }
216
217
218
    /**
219
     * @param string $requestPath
220
     *
221
     * @return bool|string
222
     */
223
    public function resolvePath($requestPath)
224
    {
225
        $filePath = $this->webroot . $requestPath;
226
227
        if ($requestPath === '/') {
228
            foreach ($this->indexFiles as $indexFile) {
229
                $indexPath = $filePath . $indexFile;
230
231
                if (file_exists($indexPath)) {
232
                    return $indexPath;
233
                }
234
            }
235
236
            return false;
237
        }
238
239
        return $filePath;
240
    }
241
242
243
    /**
244
     * @param string $filePath
245
     *
246
     * @return string
247
     */
248
    public function getContentType($filePath)
249
    {
250
        $pathInfo = pathinfo($filePath);
251
252
        if (!isset($pathInfo['extension'])) {
253
            $extension = '';
254
        } else {
255
            $extension = $pathInfo['extension'];
256
        }
257
258
        $repository = new PhpRepository();
259
        $type = $repository->findType($extension);
260
261
        if ($type === null) {
262
            $type = 'text/plain';
263
        }
264
265
        return $type;
266
    }
267
268
}
269