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 |
||
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): |
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): |
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): |
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): |
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
|
|||
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 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
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.