Issues (16)

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/Http/Curl/HttpClient.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 namespace Arcanedev\Stripe\Http\Curl;
2
3
use Arcanedev\Stripe\Contracts\Http\Curl\HttpClient as HttpClientContract;
4
use Arcanedev\Stripe\Exceptions\ApiConnectionException;
5
6
/**
7
 * Class     HttpClient
8
 *
9
 * @package  Arcanedev\Stripe\Http\Curl
10
 * @author   ARCANEDEV <[email protected]>
11
 */
12
class HttpClient implements HttpClientContract
13
{
14
    /* ------------------------------------------------------------------------------------------------
15
     |  Constants
16
     | ------------------------------------------------------------------------------------------------
17
     */
18
    const DEFAULT_TIMEOUT = 80;
19
    const DEFAULT_CONNECT_TIMEOUT = 30;
20
21
    /* ------------------------------------------------------------------------------------------------
22
     |  Properties
23
     | ------------------------------------------------------------------------------------------------
24
     */
25
    /**
26
     * The HTTP Client instance.
27
     *
28
     * @var \Arcanedev\Stripe\Http\Curl\HttpClient
29
     */
30
    private static $instance;
31
32
    /**
33
     * @var string
34
     */
35
    private $apiKey;
36
37
    /**
38
     * @var string
39
     */
40
    private $apiBaseUrl;
41
42
    /**
43
     * @var \Arcanedev\Stripe\Http\Curl\HeaderBag
44
     */
45
    private $headers;
46
47
    /**
48
     * @var \Arcanedev\Stripe\Http\Curl\CurlOptions
49
     */
50
    private $options;
51
52
    /**
53
     * @var resource
54
     */
55
    private $curl;
56
57
    /**
58
     * @var int
59
     */
60
    private $timeout = self::DEFAULT_TIMEOUT;
61
62
    /**
63
     * @var int
64
     */
65
    private $connectTimeout = self::DEFAULT_CONNECT_TIMEOUT;
66
67
    /**
68
     * @var mixed
69
     */
70
    private $response;
71
72
    /**
73
     * @var int
74
     */
75
    private $errorCode;
76
77
    /**
78
     * @var string
79
     */
80
    private $errorMessage;
81
82
    /* ------------------------------------------------------------------------------------------------
83
     |  Constructor & Destructor
84
     | ------------------------------------------------------------------------------------------------
85
     */
86
    /**
87
     * Create a HttpClient instance.
88
     */
89 4
    private function __construct()
90
    {
91 4
        $this->headers  = new HeaderBag;
92 4
        $this->options  = new CurlOptions;
93 4
        $this->response = null;
94 4
    }
95
96
    /**
97
     * Destroy the instance.
98
     */
99 2
    public function __destruct()
100
    {
101 2
        $this->close();
102 2
    }
103
104
    /* ------------------------------------------------------------------------------------------------
105
     |  Getters & Setters
106
     | ------------------------------------------------------------------------------------------------
107
     */
108
    /**
109
     * Set API Key.
110
     *
111
     * @param  string  $apiKey
112
     *
113
     * @return self
114
     */
115 298
    public function setApiKey($apiKey)
116
    {
117 298
        $this->apiKey = $apiKey;
118
119 298
        return $this;
120
    }
121
122
    /**
123
     * Set Base URL.
124
     *
125
     * @param  string  $apiBaseUrl
126
     *
127
     * @return self
128
     */
129
    public function setApiBaseUrl($apiBaseUrl)
130
    {
131
        $this->apiBaseUrl = $apiBaseUrl;
132
133
        return $this;
134
    }
135
136
    /**
137
     * Get the timeout.
138
     *
139
     * @return int
140
     */
141 2
    public function getTimeout()
142
    {
143 2
        return $this->timeout;
144
    }
145
146
    /**
147
     * Set the timeout.
148
     *
149
     * @param  int  $seconds
150
     *
151
     * @return self
152
     */
153 690
    public function setTimeout($seconds)
154
    {
155 690
        $this->timeout = (int) max($seconds, 0);
156
157 690
        return $this;
158
    }
159
160
    /**
161
     * Get the connect timeout.
162
     *
163
     * @return int
164
     */
165 2
    public function getConnectTimeout()
166
    {
167 2
        return $this->connectTimeout;
168
    }
169
170
    /**
171
     * Set the connect timeout.
172
     *
173
     * @param  int  $seconds
174
     *
175
     * @return self
176
     */
177 2
    public function setConnectTimeout($seconds)
178
    {
179 2
        $this->connectTimeout = (int) max($seconds, 0);
180
181 2
        return $this;
182
    }
183
184
    /**
185
     * Get array options.
186
     *
187
     * @return array
188
     */
189 300
    public function getOptions()
190
    {
191 300
        return $this->options->get();
192
    }
193
194
    /**
195
     * Set array options.
196
     *
197
     * @param  array  $options
198
     *
199
     * @return self
200
     */
201 300
    public function setOptionArray(array $options)
202
    {
203 300
        $this->options->setOptions($options);
204
205 300
        return $this;
206
    }
207
208
    /* ------------------------------------------------------------------------------------------------
209
     |  Curl Functions
210
     | ------------------------------------------------------------------------------------------------
211
     */
212
    /**
213
     * Init curl.
214
     */
215 298
    private function init()
216
    {
217 298
        $this->curl = curl_init();
218 298
    }
219
220
    /**
221
     * Execute curl.
222
     */
223 298
    private function execute()
224
    {
225 298
        curl_setopt_array($this->curl, $this->getOptions());
226 298
        $this->response     = curl_exec($this->curl);
227 298
        $this->errorCode    = curl_errno($this->curl);
228 298
        $this->errorMessage = curl_error($this->curl);
229 298
    }
230
231
    /**
232
     * Close curl.
233
     */
234 300
    private function close()
235
    {
236 300
        if (is_resource($this->curl))
237 298
            curl_close($this->curl);
238 300
    }
239
240
    /* ------------------------------------------------------------------------------------------------
241
     |  Main Functions
242
     | ------------------------------------------------------------------------------------------------
243
     */
244
    /**
245
     * Make the HTTP Client with options.
246
     *
247
     * @param  array  $options
248
     *
249
     * @return static
250
     */
251 2
    public static function make(array $options = [])
252
    {
253 2
        return (new static)->setOptionArray($options);
254
    }
255
256
    /**
257
     * Get the HTTP.
258
     *
259
     * @return self
260
     */
261 690
    public static function instance()
262
    {
263 690
        if ( ! self::$instance)
264 2
            self::$instance = new self;
265
266 690
        return self::$instance;
267
    }
268
269
    /**
270
     * Curl the request.
271
     *
272
     * @param  string        $method
273
     * @param  string        $url
274
     * @param  array|string  $params
275
     * @param  array         $headers
276
     * @param  bool          $hasFile
277
     *
278
     * @return array
279
     */
280 298
    public function request($method, $url, $params, $headers, $hasFile)
281
    {
282 298
        if ($method !== 'post')
283 212
            $url    = str_parse_url($url, $params);
284
        else
285 252
            $params = $hasFile ? $params : self::encode($params);
286
287 298
        $this->headers->prepare($this->apiKey, $headers, $hasFile);
288 298
        $this->options->make($method, $url, $params, $this->headers->get(), $hasFile);
0 ignored issues
show
It seems like $params can also be of type array; however, Arcanedev\Stripe\Http\Curl\CurlOptions::make() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
289 298
        $this->setOptionArray([
290 298
            CURLOPT_CONNECTTIMEOUT => $this->connectTimeout,
291 298
            CURLOPT_TIMEOUT        => $this->timeout,
292
        ]);
293
294 298
        $respHeaders = [];
295 298
        $this->prepareResponseHeaders($respHeaders);
296
297 298
        $this->init();
298 298
        $this->execute();
299 298
        $this->checkCertErrors();
300 298
        $this->checkResponse();
301
302 298
        $statusCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
303 298
        $this->close();
304
305 298
        return [$this->response, $statusCode, $respHeaders];
306
    }
307
308
    /**
309
     * Check Cert Errors.
310
     */
311 298
    private function checkCertErrors()
312
    {
313 298
        if (SslChecker::hasCertErrors($this->errorCode)) {
314
            $this->headers->set(
315
                'X-Stripe-Client-Info',
316
                '{"ca":"using Stripe-supplied CA bundle"}'
317
            );
318
319
            $this->setOptionArray([
320
                CURLOPT_HTTPHEADER => $this->headers->get(),
321
                CURLOPT_CAINFO     => SslChecker::caBundle()
322
            ]);
323
324
            $this->execute();
325
        }
326 298
    }
327
328
    /**
329
     * Encode array to query string
330
     *
331
     * @param  array|string  $array
332
     * @param  string|null   $prefix
333
     *
334
     * @return string
335
     */
336 248
    protected static function encode($array, $prefix = null)
337
    {
338
        // @codeCoverageIgnoreStart
339
        if ( ! is_array($array)) return $array;
340
        // @codeCoverageIgnoreEnd
341
342 248
        $result = [];
343
344 248
        foreach ($array as $key => $value) {
345 244
            if (is_null($value)) continue;
346
347 244
            if ($prefix)
348 112
                $key = ($key !== null && (! is_int($key) || is_array($value)))
349 112
                    ? "{$prefix}[{$key}]"
350 112
                    : "{$prefix}[]";
351
352 244
            if ( ! is_array($value)) {
353 244
                $result[] = urlencode($key) . '=' . urlencode($value);
354
            }
355 112
            elseif ($enc = self::encode($value, $key)) {
356 244
                $result[] = $enc;
357
            }
358
        }
359
360 248
        return implode('&', $result);
361
    }
362
363
    /* ------------------------------------------------------------------------------------------------
364
     |  Other Functions
365
     | ------------------------------------------------------------------------------------------------
366
     */
367
    /**
368
     * Check Response.
369
     *
370
     * @throws \Arcanedev\Stripe\Exceptions\ApiConnectionException
371
     */
372 298
    private function checkResponse()
373
    {
374 298
        if ($this->response !== false) return;
375
376
        $this->close();
377
        $this->handleCurlError();
378
    }
379
380
    /**
381
     * Handle CURL errors.
382
     *
383
     * @throws \Arcanedev\Stripe\Exceptions\ApiConnectionException
384
     */
385
    private function handleCurlError()
386
    {
387
        switch ($this->errorCode) {
388
            case CURLE_COULDNT_CONNECT:
389
            case CURLE_COULDNT_RESOLVE_HOST:
390
            case CURLE_OPERATION_TIMEOUTED:
391
                $msg = "Could not connect to Stripe ({$this->apiBaseUrl}). Please check your internet connection "
392
                    . 'and try again.  If this problem persists, you should check Stripe\'s service status at '
393
                    . 'https://twitter.com/stripestatus, or';
394
                break;
395
396
            case CURLE_SSL_CACERT:
397
            case CURLE_SSL_PEER_CERTIFICATE:
398
                $msg = 'Could not verify Stripe\'s SSL certificate.  Please make sure that your network is not '
399
                    . "intercepting certificates. (Try going to {$this->apiBaseUrl} in your browser.) "
400
                    . 'If this problem persists,';
401
                break;
402
403
            default:
404
                $msg = 'Unexpected error communicating with Stripe. If this problem persists,';
405
                // no break
406
        }
407
408
        $msg .= ' let us know at [email protected].';
409
410
        $msg .= "\n\n(Network error [errno {$this->errorCode}]: {$this->errorMessage})";
411
412
        throw new ApiConnectionException($msg);
413
    }
414
415
    /**
416
     * Prepare Response Headers.
417
     *
418
     * @param  array  $respHeaders
419
     */
420
    private function prepareResponseHeaders(array &$respHeaders)
421
    {
422 298
        $this->options->setOption(CURLOPT_HEADERFUNCTION, function ($curl, $header_line) use (&$respHeaders) {
423
            // Ignore the HTTP request line (HTTP/1.1 200 OK)
424 298
            if (strpos($header_line, ":") === false)
425 298
                return strlen($header_line);
426
427 298
            list($key, $value) = explode(":", trim($header_line), 2);
428 298
            $respHeaders[trim($key)] = trim($value);
429
430 298
            return strlen($header_line);
431 298
        });
432 298
    }
433
}
434