Issues (15)

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/Utilities/UrlQuery.php (8 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
/**
4
 * This file contains the UrlQuery class which is a wrapper for cURL
5
 *
6
 * @copyright 2015 Vladimir Jimenez
7
 * @license   https://github.com/allejo/PhpSoda/blob/master/LICENSE.md MIT
8
 */
9
10
namespace allejo\Socrata\Utilities;
11
12
use allejo\Socrata\Exceptions\CurlException;
13
use allejo\Socrata\Exceptions\HttpException;
14
use allejo\Socrata\Exceptions\SodaException;
15
16
/**
17
 * A wrapper class for working with cURL requests.
18
 *
19
 * This class configures cURL with all of the appropriate authentication information and proper cURL configuration for
20
 * processing requests.
21
 *
22
 * There's no need to access this class outside of this library as the appropriate functionality is properly wrapped in
23
 * the SodaDataset class.
24
 *
25
 * @package allejo\Socrata\Utilities
26
 * @since   0.1.0
27
 */
28
class UrlQuery
29
{
30
    /**
31
     * The default protocol the Soda API expects
32
     */
33
    const DEFAULT_PROTOCOL = "https";
34
35
    /**
36
     * The API endpoint that will be used in all requests
37
     *
38
     * @var string
39
     */
40
    private $url;
41
42
    /**
43
     * The cURL object this class is a wrapper for
44
     *
45
     * @var resource
46
     */
47
    private $cURL;
48
49
    /**
50
     * The Socrata API token
51
     *
52
     * @var string
53
     */
54
    private $token;
55
56
    /**
57
     * HTTP headers sent in all requests
58
     *
59
     * @var string[]
60
     */
61
    private $headers;
62
63
    /**
64
     * The OAuth 2.0 token sent in all requests
65
     *
66
     * @var string
67
     */
68
    private $oAuth2Token;
69
70
    /**
71
     * Configure all of the authentication needed for cURL requests and the API endpoint
72
     *
73
     * **Note** If OAuth 2.0 is used for authentication, do not give values to the $email and $password parameters;
74
     * instead, use the `setOAuth2Token()` function. An API token will still be required to bypass throttling.
75
     *
76
     * @param string $url      The API endpoint this instance will be calling
77
     * @param string $token    The API token used in order to bypass throttling
78
     * @param string $email    The email address of the user being authenticated through Basic Authentication
79
     * @param string $password The password for the user being authenticated through Basic Authentication
80
     *
81
     * @see   setOAuth2Token
82
     *
83
     * @since 0.1.0
84
     */
85
    public function __construct ($url, $token = "", $email = "", $password = "")
86
    {
87
        $this->url   = $url;
88
        $this->token = $token;
89
        $this->cURL  = curl_init();
90
91
        // Build up the headers we'll need to pass
92
        $this->headers = array(
93
                             'Accept: application/json',
94
                             'Content-type: application/json',
95
                             'X-App-Token: ' . $this->token
96
                         );
97
98
        $this->configureCurl($email, $password);
99
    }
100
101
    /**
102
     * Clean up after ourselves; clean up the cURL object.
103
     */
104
    public function __destruct ()
105
    {
106
        curl_close($this->cURL);
107
    }
108
109
    /**
110
     * Set the OAuth 2.0 token that requests will be using. This function does **not** retrieve a token, it simply uses
111
     * the existing token and sends it as authentication.
112
     *
113
     * @param string $token The OAuth 2.0 token used in requests
114
     *
115
     * @since 0.1.2
116
     */
117
    public function setOAuth2Token ($token)
118
    {
119
        if (!StringUtilities::isNullOrEmpty($token))
120
        {
121
            $this->oAuth2Token = $token;
122
            $this->headers[]   = "Authorization: OAuth " . $this->oAuth2Token;
123
        }
124
    }
125
126
    /**
127
     * Send a GET request
128
     *
129
     * @param  string $params           The GET parameters to be appended to the API endpoint
130
     * @param  bool   $associativeArray When true, the returned data will be associative arrays; otherwise, it'll be an
131
     *                                  StdClass object.
132
     * @param  array  $headers          An array where the return HTTP headers will be stored
0 ignored issues
show
Should the type for parameter $headers not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
133
     *
134
     * @see    SodaClient::enableAssociativeArrays
135
     *
136
     * @since  0.1.0
137
     *
138
     * @return mixed  An associative array matching the returned JSON result or an StdClass object
139
     */
140
    public function sendGet ($params, $associativeArray, &$headers = NULL)
141
    {
142
        if (is_array($params))
143
        {
144
            $parameters = self::formatParameters($params);
145
            $full_url   = self::buildQuery($this->url, $parameters);
146
        }
147
        else if (!empty($params))
148
        {
149
            $full_url = $this->url . "?" . $params;
150
        }
151
        else
152
        {
153
            $full_url = $this->url;
154
        }
155
156
        curl_setopt($this->cURL, CURLOPT_URL, $full_url);
157
158
        return $this->handleQuery($associativeArray, $headers);
0 ignored issues
show
It seems like $headers defined by parameter $headers on line 140 can also be of type null; however, allejo\Socrata\Utilities\UrlQuery::handleQuery() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
159
    }
160
161
    /**
162
     * Send a POST request
163
     *
164
     * @param  string $dataAsJson       The data that will be sent to Socrata as JSON
165
     * @param  bool   $associativeArray When true, the returned data will be associative arrays; otherwise, it'll be an
166
     *                                  StdClass object.
167
     * @param  array  $headers          An array where the return HTTP headers will be stored
0 ignored issues
show
Should the type for parameter $headers not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
168
     *
169
     * @see    SodaClient::enableAssociativeArrays
170
     *
171
     * @since  0.1.0
172
     *
173
     * @return mixed  An associative array matching the returned JSON result or an StdClass object
174
     */
175
    public function sendPost ($dataAsJson, $associativeArray, &$headers = NULL)
176
    {
177
        $this->setPostFields($dataAsJson);
178
179
        curl_setopt_array($this->cURL, array(
180
            CURLOPT_POST => true,
181
            CURLOPT_CUSTOMREQUEST => "POST"
182
        ));
183
184
        return $this->handleQuery($associativeArray, $headers);
0 ignored issues
show
It seems like $headers defined by parameter $headers on line 175 can also be of type null; however, allejo\Socrata\Utilities\UrlQuery::handleQuery() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
185
    }
186
187
    /**
188
     * Send a PUT request
189
     *
190
     * @param  string $dataAsJson       The data that will be sent to Socrata as JSON
191
     * @param  bool   $associativeArray When true, the returned data will be associative arrays; otherwise, it'll be an
192
     *                                  StdClass object.
193
     * @param  array  $headers          An array where the return HTTP headers will be stored
0 ignored issues
show
Should the type for parameter $headers not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
194
     *
195
     * @see    SodaClient::enableAssociativeArrays
196
     *
197
     * @since  0.1.0
198
     *
199
     * @return mixed  An associative array matching the returned JSON result or an StdClass object
200
     */
201
    public function sendPut ($dataAsJson, $associativeArray, &$headers = NULL)
202
    {
203
        $this->setPostFields($dataAsJson);
204
205
        curl_setopt($this->cURL, CURLOPT_CUSTOMREQUEST, "PUT");
206
207
        return $this->handleQuery($associativeArray, $headers);
0 ignored issues
show
It seems like $headers defined by parameter $headers on line 201 can also be of type null; however, allejo\Socrata\Utilities\UrlQuery::handleQuery() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
208
    }
209
210
    /**
211
     * Send a DELETE request
212
     *
213
     * @param  bool   $associativeArray When true, the returned data will be associative arrays; otherwise, it'll be an
214
     *                                  StdClass object.
215
     * @param  array  $headers          An array where the return HTTP headers will be stored
0 ignored issues
show
Should the type for parameter $headers not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
216
     *
217
     * @see    SodaClient::enableAssociativeArrays
218
     *
219
     * @since  0.1.2
220
     *
221
     * @return mixed  An associative array matching the returned JSON result or an StdClass object
222
     */
223
    public function sendDelete ($associativeArray, &$headers = NULL)
224
    {
225
        curl_setopt($this->cURL, CURLOPT_CUSTOMREQUEST, "DELETE");
226
227
        $this->handleQuery($associativeArray, $headers, true);
0 ignored issues
show
It seems like $headers defined by parameter $headers on line 223 can also be of type null; however, allejo\Socrata\Utilities\UrlQuery::handleQuery() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
228
    }
229
230
    /**
231
     * Set the POST fields that will be submitted in the cURL request
232
     *
233
     * @param string $dataAsJson The data that will be sent to Socrata as JSON
234
     *
235
     * @since 0.1.0
236
     */
237
    private function setPostFields ($dataAsJson)
238
    {
239
        curl_setopt($this->cURL, CURLOPT_POSTFIELDS, $dataAsJson);
240
    }
241
242
    /**
243
     * Handle the execution of the cURL request. This function will also save the returned HTTP headers and handle them
244
     * appropriately.
245
     *
246
     * @param  bool  $associativeArray When true, the returned data will be associative arrays; otherwise, it'll be an
247
     *                                 StdClass object.
248
     * @param  array $headers          The reference to the array where the returned HTTP headers will be stored
249
     * @param  bool  $ignoreReturn     True if the returned body should be ignored
250
     *
251
     * @since  0.1.0
252
     *
253
     * @throws \allejo\Socrata\Exceptions\CurlException If cURL is misconfigured or encounters an error
254
     * @throws \allejo\Socrata\Exceptions\HttpException An HTTP status of something other 200 is returned
255
     * @throws \allejo\Socrata\Exceptions\SodaException A SODA API error is returned
256
     *
257
     * @return mixed|NULL
258
     */
259
    private function handleQuery ($associativeArray, &$headers, $ignoreReturn = false)
260
    {
261
        $result = $this->executeCurl();
262
263
        // Ignore "100 Continue" headers
264
        $continueHeader = "HTTP/1.1 100 Continue\r\n\r\n";
265
266
        if (strpos($result, $continueHeader) === 0)
267
        {
268
            $result = str_replace($continueHeader, '', $result);
269
        }
270
271
        list($header, $body) = explode("\r\n\r\n", $result, 2);
272
273
        $this->saveHeaders($header, $headers);
274
275
        if ($ignoreReturn)
276
        {
277
            return NULL;
278
        }
279
280
        $resultArray = $this->handleResponseBody($body, $result);
281
282
        return ($associativeArray) ? $resultArray : json_decode($body, false);
283
    }
284
285
    /**
286
     * Configure the cURL instance and its credentials for Basic Authentication that this instance will be working with
287
     *
288
     * @param string $email    The email for the user with Basic Authentication
289
     * @param string $password The password for the user with Basic Authentication
290
     *
291
     * @since 0.1.0
292
     */
293
    private function configureCurl ($email, $password)
294
    {
295
        curl_setopt_array($this->cURL, array(
296
            CURLOPT_URL => $this->url,
297
            CURLOPT_HEADER => true,
298
            CURLOPT_HTTPHEADER => $this->headers,
299
            CURLOPT_RETURNTRANSFER => true,
300
            CURLOPT_SSLVERSION => 6
301
        ));
302
303
        if (!StringUtilities::isNullOrEmpty($email) && !StringUtilities::isNullOrEmpty($password))
304
        {
305
            curl_setopt_array($this->cURL, array(
306
                CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
307
                CURLOPT_USERPWD => $email . ":" . $password
308
            ));
309
        }
310
    }
311
312
    /**
313
     * Execute the finalized cURL object that has already been configured
314
     *
315
     * @since  0.1.0
316
     *
317
     * @throws \allejo\Socrata\Exceptions\CurlException If cURL is misconfigured or encounters an error
318
     *
319
     * @return mixed
320
     */
321
    private function executeCurl ()
322
    {
323
        $result = curl_exec($this->cURL);
324
325
        if (!$result)
326
        {
327
            throw new CurlException($this->cURL);
328
        }
329
330
        return $result;
331
    }
332
333
    /**
334
     * Check for unexpected errors or SODA API errors
335
     *
336
     * @param  string $body   The body of the response
337
     * @param  string $result The unfiltered result cURL received
338
     *
339
     * @since  0.1.0
340
     *
341
     * @throws \allejo\Socrata\Exceptions\HttpException If the $body returned was not a JSON object
342
     * @throws \allejo\Socrata\Exceptions\SodaException The returned JSON object in the $body was a SODA API error
343
     *
344
     * @return mixed An associative array of the decoded JSON response
345
     */
346
    private function handleResponseBody ($body, $result)
347
    {
348
        // We somehow got a server error from Socrata without a JSON object with details
349
        if (!StringUtilities::isJson($body))
350
        {
351
            $httpCode = curl_getinfo($this->cURL, CURLINFO_HTTP_CODE);
352
353
            throw new HttpException($httpCode, $result);
354
        }
355
356
        $resultArray = json_decode($body, true);
357
358
        // We got an error JSON object back from Socrata
359
        if (array_key_exists('error', $resultArray) && $resultArray['error'])
360
        {
361
            throw new SodaException($resultArray);
362
        }
363
364
        return $resultArray;
365
    }
366
367
    /**
368
     * Handle the returned HTTP headers and save them into an array
369
     *
370
     * @param string $header  The returned HTTP headers
371
     * @param array  $headers The reference to the array where our headers will be saved
372
     *
373
     * @since 0.1.0
374
     */
375
    private function saveHeaders ($header, &$headers)
376
    {
377
        if ($headers === NULL)
378
        {
379
            return;
380
        }
381
382
        $header       = explode("\r\n", $header);
383
        $headers      = array();
384
        $headerLength = count($header);
385
386
        // The 1st element is the HTTP code, so we can safely skip it
387
        for ($i = 1; $i < $headerLength; $i++)
388
        {
389
            list($key, $val) = explode(":", $header[$i]);
390
            $headers[$key] = trim($val);
391
        }
392
    }
393
394
    /**
395
     * Build a URL with GET parameters formatted into the URL
396
     *
397
     * @param string  $url    The base URL
398
     * @param array   $params The GET parameters that need to be appended to the base URL
399
     *
400
     * @since 0.1.0
401
     *
402
     * @return string A URL with GET parameters
403
     */
404
    private static function buildQuery ($url, $params = array())
405
    {
406
        $full_url = $url;
407
408
        if (count($params) > 0)
409
        {
410
            $full_url .= "?" . implode("&", $params);
411
        }
412
413
        return $full_url;
414
    }
415
416
    /**
417
     * Format an array into a URL encoded values to be submitted in cURL requests
418
     *
419
     * **Input**
420
     *
421
     * ```php
422
     * array(
423
     *     "foo"   => "bar",
424
     *     "param" => "value"
425
     * )
426
     * ```
427
     *
428
     * **Output**
429
     *
430
     * ```php
431
     * array(
432
     *     "foo=bar",
433
     *     "param=value"
434
     * )
435
     * ```
436
     *
437
     * @param  array    $params An array containing parameter names as keys and parameter values as values in the array.
438
     *
439
     * @return string[]         A URL encoded and combined array of GET parameters to be sent
440
     */
441
    private static function formatParameters ($params)
442
    {
443
        $parameters = array();
444
445
        foreach ($params as $key => $value)
446
        {
447
            $parameters[] = rawurlencode($key) . "=" . rawurlencode($value);
448
        }
449
450
        return $parameters;
451
    }
452
}
453