This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Germania\IpstackClient; |
||
3 | |||
4 | use Germania\IpstackClient\IpstackClientInterface; |
||
5 | use Germania\IpstackClient\IpstackExceptionInterface; |
||
6 | |||
7 | use GuzzleHttp\Psr7\Response as GuzzleResponse; |
||
8 | |||
9 | use Psr\Http\Server\MiddlewareInterface; |
||
10 | use Psr\Http\Server\RequestHandlerInterface; |
||
11 | use Psr\Http\Message\ResponseInterface; |
||
12 | use Psr\Http\Message\ServerRequestInterface; |
||
13 | |||
14 | use Psr\Log\LoggerInterface; |
||
15 | use Psr\Log\LoggerAwareTrait; |
||
16 | use Psr\Log\NullLogger; |
||
17 | |||
18 | |||
19 | /** |
||
20 | * This Slim-style "Double Pass" middleware finds out the country where the client comes from |
||
21 | * and stores the country code (DE or CH) with PSR-7 Request attribute. |
||
22 | * |
||
23 | * Requirement: |
||
24 | * |
||
25 | * This middleware requires a ServerRequest attribute called "ip_address" |
||
26 | * as provided by akrabat's Slim Client IP address middleware |
||
27 | * - which therefore must be executed before this one! |
||
28 | * |
||
29 | * https://github.com/akrabat/ip-address-middleware |
||
30 | * |
||
31 | * Basic conecpts: |
||
32 | * |
||
33 | * IP to Geolocation: |
||
34 | * This class requires an IpstackClient instance provided by germania-kg/ipstack. |
||
35 | * which asks the "IP to Geolocation" API from ipstack (https://ipstack.com). |
||
36 | * Since we currently are using the "free plan", usage is limited to 10.000 API calls per month. |
||
37 | */ |
||
38 | class IpstackMiddleware implements MiddlewareInterface |
||
39 | { |
||
40 | use LoggerAwareTrait; |
||
41 | |||
42 | |||
43 | /** |
||
44 | * Maps ipstack response fields to Request attribute Names. |
||
45 | * |
||
46 | * Array keys are ipstack fields, values are attribute names, |
||
47 | * for example: |
||
48 | * |
||
49 | * array( |
||
50 | * "country_code" => "X-IpstackCountryCode", |
||
51 | * "language" => "X-IpstackLanguage" |
||
52 | * ); |
||
53 | * |
||
54 | * @var array |
||
55 | */ |
||
56 | public $ipstack_attributes = array(); |
||
57 | |||
58 | |||
59 | /** |
||
60 | * Specifies the ipstack response fields that will be requested always. |
||
61 | * |
||
62 | * @see https://ipstack.com/documentation#fields |
||
63 | * @var array |
||
64 | */ |
||
65 | public $ipstack_default_fields = array("ip", "country_code", "country_name"); |
||
66 | |||
67 | |||
68 | /** |
||
69 | * Request attribute with IP address as described here: |
||
70 | * http://www.slimframework.com/docs/v3/cookbook/ip-address.html |
||
71 | * |
||
72 | * @var string |
||
73 | */ |
||
74 | public $ip_address_attribute = "ip_address"; |
||
75 | |||
76 | |||
77 | /** |
||
78 | * Request attribute to store the full ipstack information |
||
79 | * |
||
80 | * @var string |
||
81 | */ |
||
82 | public $ipstack_attribute = "ipstack"; |
||
83 | |||
84 | |||
85 | /** |
||
86 | * @var IpstackClientInterface |
||
87 | */ |
||
88 | public $ipstack_client; |
||
89 | |||
90 | |||
91 | /** |
||
92 | * @var integer |
||
93 | */ |
||
94 | public $reponse_error_code = 400; |
||
95 | |||
96 | |||
97 | |||
98 | /** |
||
99 | * @var string |
||
100 | */ |
||
101 | public $success_loglevel = "notice"; |
||
102 | |||
103 | |||
104 | /** |
||
105 | * @var string |
||
106 | */ |
||
107 | public $invalid_ip_loglevel = "error"; |
||
108 | |||
109 | |||
110 | /** |
||
111 | * @var string |
||
112 | */ |
||
113 | public $ipstack_error_loglevel = "error"; |
||
114 | |||
115 | |||
116 | /** |
||
117 | * @param IpstackClientInterface $ipstack_client IpstackClient |
||
118 | * @param string $ip_address_attribute Optional: Request attribute name with Client IP address |
||
119 | * @param array $request_attributes Optional: Map ipstack fields to request attributes |
||
0 ignored issues
–
show
|
|||
120 | * @param LoggerInterface|null $logger Optional: PSR-3 Logger |
||
121 | */ |
||
122 | 48 | public function __construct( IpstackClientInterface $ipstack_client, string $ip_address_attribute = null, array $ipstack_attributes = array(), LoggerInterface $logger = null, string $success_loglevel = null , string $invalid_ip_loglevel = null , string $ipstack_error_loglevel = null ) |
|
123 | { |
||
124 | 48 | $this->ipstack_client = $ipstack_client; |
|
125 | 48 | $this->ip_address_attribute = $ip_address_attribute; |
|
126 | 48 | $this->ipstack_attributes = $ipstack_attributes; |
|
127 | 48 | $this->success_loglevel = $success_loglevel ?: $this->success_loglevel; |
|
128 | 48 | $this->invalid_ip_loglevel = $invalid_ip_loglevel ?: $this->invalid_ip_loglevel; |
|
129 | 48 | $this->ipstack_error_loglevel = $ipstack_error_loglevel ?: $this->ipstack_error_loglevel; |
|
130 | |||
131 | 48 | $this->setLogger( $logger ?: new NullLogger ); |
|
132 | 48 | } |
|
133 | |||
134 | |||
135 | /** |
||
136 | * PSR-15 "Single pass" pattern |
||
137 | * |
||
138 | * @param ServerRequestInterface $request [description] |
||
139 | * @param RequestHandlerInterface $handler [description] |
||
140 | * @return ResponseInterface |
||
141 | */ |
||
142 | 40 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface |
|
143 | { |
||
144 | 40 | $ipstack = $this->business( $request); |
|
145 | 40 | View Code Duplication | if (false === $ipstack): |
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. ![]() |
|||
146 | 24 | $m = sprintf("Could not determine client IP, force status '%s' response", $this->reponse_error_code); |
|
147 | 24 | $this->logger->log($this->invalid_ip_loglevel, $m); |
|
148 | 24 | return new GuzzleResponse( $this->reponse_error_code ); |
|
149 | endif; |
||
150 | |||
151 | 16 | $request = $request->withAttribute( $this->ipstack_attribute, $ipstack); |
|
152 | // Map certain ipstack fields to custom request attributes |
||
153 | 16 | View Code Duplication | foreach( $this->ipstack_attributes as $field => $attr_name): |
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. ![]() |
|||
154 | $request = $request->withAttribute($attr_name, $ipstack[ $field ] ?? null ); |
||
155 | endforeach; |
||
156 | |||
157 | // Call $handler, return response |
||
158 | 16 | return $handler->handle($request); |
|
159 | } |
||
160 | |||
161 | |||
162 | |||
163 | /** |
||
164 | * Slim3-style "Double Pass" pattern |
||
165 | * |
||
166 | * @param ServerRequestInterface $request |
||
167 | * @param ResponseInterface $response |
||
168 | * @param callable $next |
||
169 | * |
||
170 | * @return ResponseInterface |
||
171 | */ |
||
172 | 40 | public function __invoke( ServerRequestInterface $request, ResponseInterface $response, callable $next ) |
|
173 | { |
||
174 | 40 | $ipstack = $this->business( $request); |
|
175 | 40 | View Code Duplication | if (false === $ipstack): |
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. ![]() |
|||
176 | 24 | $m = sprintf("Could not determine client IP, force status '%s' response", $this->reponse_error_code); |
|
177 | 24 | $this->logger->log($this->invalid_ip_loglevel, $m); |
|
178 | 24 | return $response->withStatus( $this->reponse_error_code ); |
|
179 | endif; |
||
180 | |||
181 | 16 | $request = $request->withAttribute( $this->ipstack_attribute, $ipstack); |
|
182 | // Map certain ipstack fields to custom request attributes |
||
183 | 16 | View Code Duplication | foreach( $this->ipstack_attributes as $field => $attr_name): |
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. ![]() |
|||
184 | $request = $request->withAttribute($attr_name, $ipstack[ $field ] ?? null ); |
||
185 | endforeach; |
||
186 | |||
187 | |||
188 | // Call $next middleware, return response |
||
189 | 16 | return $next($request, $response); |
|
190 | } |
||
191 | |||
192 | |||
193 | |||
194 | /** |
||
195 | * Perfoms the middelware action |
||
196 | * |
||
197 | * @param ServerRequestInterface $request |
||
198 | * @return bool |
||
199 | */ |
||
200 | 40 | protected function business( ServerRequestInterface $request ) |
|
201 | { |
||
202 | 40 | $client_ip = $this->getClientIp( $request ); |
|
203 | |||
204 | 40 | if (!$this->assertClientIp( $client_ip )): |
|
205 | 24 | return false; |
|
206 | endif; |
||
207 | |||
208 | // Ask IpstackClient and store result in Request |
||
209 | 16 | $ipstack = $this->askIpStack( $client_ip ); |
|
210 | |||
211 | 16 | return $ipstack; |
|
212 | } |
||
213 | |||
214 | |||
215 | |||
216 | /** |
||
217 | * Returns the client's IP, either from request attribute name or REMOTE_ADDR. |
||
218 | * |
||
219 | * @param ServerRequestInterface $request The request |
||
220 | * @return string Client IP address string |
||
221 | */ |
||
222 | 40 | protected function getClientIp(ServerRequestInterface $request) : string |
|
223 | { |
||
224 | 40 | if (!empty($this->ip_address_attribute)): |
|
225 | 20 | $client_ip = $request->getAttribute( $this->ip_address_attribute ); |
|
226 | 20 | $log_msg = "Use IP from Request attribute"; |
|
227 | 20 | $ip_src = $this->ip_address_attribute; |
|
228 | else: |
||
229 | 20 | $serverParams = $request->getServerParams(); |
|
230 | 20 | $client_ip = $serverParams['REMOTE_ADDR'] ?? ""; |
|
231 | |||
232 | 20 | $log_msg = "Use IP from SERVER"; |
|
233 | 20 | $ip_src = "REMOTE_ADDR"; |
|
234 | endif; |
||
235 | |||
236 | 40 | $this->logger->debug($log_msg, [ |
|
237 | 40 | 'src' => $ip_src, |
|
238 | 40 | 'clientIp' => $client_ip |
|
239 | ]); |
||
240 | |||
241 | 40 | return $client_ip ?: ""; |
|
242 | } |
||
243 | |||
244 | |||
245 | |||
246 | /** |
||
247 | * Asks the ipstack API about information for the given IP. |
||
248 | * |
||
249 | * If something goes wrong, an array with default values |
||
250 | * will be returned. |
||
251 | * |
||
252 | * @param string $client_ip |
||
253 | * @return array ipstack response excerpt. |
||
254 | */ |
||
255 | 16 | protected function askIpStack( string $client_ip ) : array |
|
256 | { |
||
257 | // Prepare result set |
||
258 | 16 | $custom_fields = array_keys( $this->ipstack_attributes ); |
|
259 | 16 | $fields = array_merge($custom_fields, $this->ipstack_default_fields); |
|
260 | |||
261 | 16 | $default_return = array_fill_keys($fields, null); |
|
262 | 16 | $default_return['ip'] = $client_ip; |
|
263 | |||
264 | // The business |
||
265 | try { |
||
266 | 16 | $ipstack = $this->ipstack_client->get( $client_ip, [ |
|
0 ignored issues
–
show
The call to
IpstackClientInterface::get() has too many arguments starting with array('fields' => join('...s), 'language' => 'de') .
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the ![]() |
|||
267 | 16 | "fields" => join(",", $fields), |
|
268 | 16 | "language" => "de" |
|
269 | ]); |
||
270 | |||
271 | // Log things. Make sure to log only default fields |
||
272 | // See "$ipstack_default_fields" |
||
273 | 16 | $this->logger->log($this->success_loglevel, "Success: ipstack response", [ |
|
274 | 16 | 'clientIp' => $ipstack['ip'], |
|
275 | 16 | 'countryCode' => $ipstack['country_code'], |
|
276 | 16 | 'countryName' => $ipstack['country_name'], |
|
277 | ]); |
||
278 | |||
279 | // Merge ipstack response |
||
280 | 16 | $result = array_merge($default_return, $ipstack); |
|
281 | 16 | return $result; |
|
282 | |||
283 | } |
||
284 | catch (IpstackExceptionInterface $e) { |
||
285 | $this->logger->log($this->ipstack_error_loglevel, "Asking ipstack failed", [ |
||
286 | 'clientIp' => $client_ip, |
||
287 | 'exceptionClass' => get_class($e), |
||
288 | 'exceptionMessage' => $e->getMessage() |
||
289 | ]); |
||
290 | |||
291 | // At least: |
||
292 | return $default_return; |
||
293 | } |
||
294 | |||
295 | } |
||
296 | |||
297 | |||
298 | |||
299 | /** |
||
300 | * Checks wether a given IP address is not empty and valid IPv4 or IP46. |
||
301 | * |
||
302 | * @param string $client_ip |
||
303 | * @return bool |
||
304 | */ |
||
305 | 40 | protected function assertClientIp( string $client_ip ) : bool |
|
306 | { |
||
307 | |||
308 | 40 | if (!$client_ip) : |
|
309 | 16 | $this->logger->error("Empty IP given?!"); |
|
310 | 16 | return false; |
|
311 | endif; |
||
312 | |||
313 | 24 | if( filter_var($client_ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) : |
|
314 | 8 | $this->logger->debug("Valid IPv4 address"); |
|
315 | 8 | return true; |
|
316 | |||
317 | 16 | elseif( filter_var($client_ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) : |
|
318 | 8 | $this->logger->debug("Valid IPv6 address"); |
|
319 | 8 | return true; |
|
320 | |||
321 | endif; |
||
322 | |||
323 | 8 | $this->logger->warning("Client IP is neither IPv4 nor IPv6"); |
|
324 | 8 | return false; |
|
325 | } |
||
326 | |||
327 | } |
||
328 |
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.
Consider the following example. The parameter
$italy
is not defined by the methodfinale(...)
.The most likely cause is that the parameter was removed, but the annotation was not.