Issues (19)

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/Connection.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
 * For the full copyright and license information, please view the LICENSE
4
 * file that was distributed with this source code.
5
 *
6
 * @author Nikita Vershinin <[email protected]>
7
 * @license MIT
8
 */
9
namespace OpenStackStorage;
10
11
define('CONTAINER_NAME_LIMIT', 256);
12
define('OBJECT_NAME_LIMIT', 1024);
13
define('META_NAME_LIMIT', 128);
14
define('META_VALUE_LIMIT', 256);
15
16
/**
17
 * Manages the connection to the storage system and serves as a factory
18
 * for \OpenStackStorage\Container instances.
19
 */
20
class Connection
21
{
22
23
    /**
24
     * Use Rackspace servicenet to access Cloud Files.
25
     *
26
     * @var boolean
27
     */
28
    protected $useServicenet = false;
29
30
    /**
31
     * User-Agent for request.
32
     *
33
     * @var string
34
     */
35
    protected $userAgent = 'PHP OpenStackStorage';
36
37
    /**
38
     * Request timeout.
39
     *
40
     * @var integer
41
     */
42
    protected $timeout = 5;
43
44
    /**
45
     * Authentication object.
46
     *
47
     * @var \OpenStackStorage\Authentication
48
     */
49
    protected $auth = null;
50
51
    /**
52
     * Authentication has already been processed.
53
     *
54
     * @var boolean
55
     */
56
    protected $isAuthenticated = false;
57
58
    /**
59
     * Authentication token.
60
     *
61
     * @var string
62
     */
63
    protected $authToken = null;
64
65
    /**
66
     * Array with information about the connection URI.
67
     *
68
     * @var array
69
     */
70
    protected $connectionUrlInfo = null;
71
72
    /**
73
     * HTTP-client to work with storage.
74
     *
75
     * @var \OpenStackStorage\Client
76
     */
77
    protected $client = null;
78
79
    /**
80
     * CDN connection URL.
81
     *
82
     * @var string
83
     */
84
    protected $cdnUrl = null;
85
86
    /**
87
     * Is the access via CDN enabled.
88
     *
89
     * @var boolean
90
     */
91
    protected $cdnEnabled = false;
92
93
    /**
94
     * HTTP-client to work with storage.
95
     *
96
     * @var \OpenStackStorage\Client
97
     */
98
    protected $cdnClient = null;
99
100
    /**
101
     * List of parameters that are allowed to be used in the GET-requests to
102
     * fetch information about the containers:
103
     *  — limit      For an integer value n, limits the number of results
104
     *               to n values.
105
     *  — marker     Given a string value x, return container names greater
106
     *               in value than the specified marker.
107
     *  — end_marker Given a string value x, return container names less
108
     *               in value than the specified marker.
109
     *  — format     Response format (json, xml, plain).
110
     *
111
     * @link http://docs.openstack.org/api/openstack-object-storage/1.0/content/s_listcontainers.html
112
     * @var array
113
     */
114
    protected static $allowedParameters = array(
115
        'limit',
116
        'marker',
117
        'end_marker',
118
        'format',
119
    );
120
121
    /**
122
     * Local cache of requests to fetch list of containers.
123
     *
124
     * @var array
125
     */
126
    protected static $listContainersCache = array();
127
128
    /**
129
     * The class constructor.
130
     *
131
     * @param  string                    $username
132
     * @param  string                    $apiKey
133
     * @param  array                     $options
134
     * @param  integer                   $timeout
135
     * @throws \InvalidArgumentException
136
     */
137
    public function __construct($username, $apiKey, $options = array(), $timeout = 5)
138
    {
139
        $this->timeout = intval($timeout);
140
141
        // If the environement variable RACKSPACE_SERVICENET is set (to
142
        // anything) it will automatically set $useServicenet=true
143
        if (array_key_exists('servicenet', $options)) {
144
            $this->useServicenet = (boolean) $options['servicenet'];
145
        } else {
146
            $this->useServicenet = (boolean) getenv('RACKSPACE_SERVICENET');
147
        }
148
149
        if (!empty($options['useragent'])) {
150
            $this->userAgent = strval($options['useragent']);
151
        }
152
153
        // Authentication
154
        if (empty($options['authurl'])) {
155
            throw new \InvalidArgumentException(
156
                'Incorrect or invalid arguments supplied'
157
            );
158
        }
159
160
        $this->auth = new Authentication($username, $apiKey, $options['authurl'], $this->userAgent, $timeout);
161
    }
162
163
    /**
164
     * Return the value of the $authToken property.
165
     *
166
     * @return string
167
     */
168
    public function getAuthToken()
169
    {
170
        $this->authenticate();
171
172
        return $this->authToken;
173
    }
174
175
    /**
176
     * Return the value of the $cdnEnabled property.
177
     *
178
     * @return boolean
179
     */
180
    public function getCdnEnabled()
181
    {
182
        return $this->cdnEnabled;
183
    }
184
185
    /**
186
     * Return the value of the $connection property.
187
     *
188
     * @return \OpenStackStorage\Client
189
     */
190
    public function getClient()
191
    {
192
        return $this->client;
193
    }
194
195
    /**
196
     * Return the value of the $timeout property.
197
     *
198
     * @return integer
199
     */
200
    public function getTimeout()
201
    {
202
        return $this->timeout;
203
    }
204
205
    /**
206
     * Return the value of the $userAgent property.
207
     *
208
     * @return string
209
     */
210
    public function getUserAgent()
211
    {
212
        return $this->userAgent;
213
    }
214
215
    /**
216
     * Performs an http request to the storage.
217
     *
218
     * @param  string $method     name of the method (i.e. GET, PUT, POST, etc)
219
     * @param  array  $path       list of tokens that will be added to connection
220
     *                            URI string
221
     * @param  array  $headers    additional headers
222
     * @param  array  $parameters additional parameters that will be added to the
223
     *                            query string
224
     * @return array
225
     */
226
    public function makeRequest($method, array $path = array(), array $headers = array(), $parameters = array())
227
    {
228
        $this->authenticate();
229
230
        return $this->makeRealRequest($this->client, $method, $this->getPathFromArray($path), $parameters, $headers);
231
    }
232
233
    /**
234
     * Performs an http request to the CDN.
235
     *
236
     * @param  string                                     $method  name of the method (i.e. GET, PUT, POST, etc)
237
     * @param  array                                      $path    list of tokens that will be added to connection
238
     *                                                             URI string
239
     * @param  array                                      $headers additional headers
240
     * @return array
241
     * @throws \OpenStackStorage\Exceptions\CDNNotEnabled
242
     */
243
    public function makeCdnRequest($method, array $path = array(), array $headers = array())
244
    {
245
        $this->authenticate();
246
247
        if (!$this->getCdnEnabled()) {
248
            throw new Exceptions\CDNNotEnabled();
249
        }
250
251
        return $this->makeRealRequest($this->cdnClient, $method, $this->getPathFromArray($path), $headers);
252
    }
253
254
    /**
255
     * Return array with number of containers, total bytes in the account and
256
     * account metadata.
257
     *
258
     * @return array
259
     */
260
    public function getAccountInfo()
261
    {
262
        $response     = $this->makeRequest(Client::HEAD);
263
        $nbContainers = 0;
264
        $totalSize    = 0;
265
        $metadata     = array();
266
267
        foreach ($response['headers'] as $name => $value) {
0 ignored issues
show
The expression $response['headers'] of type string is not traversable.
Loading history...
268
            $name = strtolower($name);
269
270
            if (0 === strcmp($name, 'x-account-container-count')) {
271
                $nbContainers = intval($value);
272
            } elseif (0 === strcmp($name, 'x-account-bytes-used')) {
273
                $totalSize = intval($value);
274
            } elseif (0 === strpos($name, 'x-account-meta-')) {
275
                $metadata[substr($name, 15)] = $value;
276
            }
277
        }
278
279
        return array(
280
            $nbContainers,
281
            $totalSize,
282
            $metadata
283
        );
284
    }
285
286
    /**
287
     * Update account metadata.
288
     *
289
     * Example:
290
     * <code>
291
     * $connection->updateAccountMetadata(array(
292
     *     'X-Account-Meta-Foo' => 'bar',
293
     * ));
294
     * </code>
295
     *
296
     * @param array $metadata
297
     */
298
    public function updateAccountMetadata(array $metadata)
299
    {
300
        $this->makeRequest(Client::POST, array(), $metadata);
301
    }
302
303
    /**
304
     * Create new container.
305
     *
306
     * If $errorOnExisting is true and container already exists,
307
     * throws \OpenStackStorage\Exceptions\ContainerExists.
308
     *
309
     * @param  string                                       $name
310
     * @param  boolean                                      $errorOnExisting
311
     * @return \OpenStackStorage\Container
312
     * @throws \OpenStackStorage\Exceptions\ContainerExists
313
     */
314
    public function createContainer($name, $errorOnExisting = false)
315
    {
316
        $this->validateContainerName($name);
317
318
        $response = $this->makeRequest(Client::PUT, array($name), array('Content-Length' => 0));
319
        if ($errorOnExisting && 202 == $response['status']) {
320
            throw new Exceptions\ContainerExists($name);
321
        }
322
323
        return new Container($this, $name);
324
    }
325
326
    /**
327
     * Delete container.
328
     *
329
     * @param  \OpenStackStorage\Container|string                    $container
330
     * @throws \OpenStackStorage\Exceptions\NoSuchContainer
331
     * @throws \Exception|\OpenStackStorage\Exceptions\ResponseError
332
     * @throws \OpenStackStorage\Exceptions\ContainerNotEmpty
333
     */
334
    public function deleteContainer($container)
335
    {
336
        if (is_object($container) && $container instanceof Container) {
337
            $name = $container->getName();
338
        } else {
339
            $name = strval($container);
340
        }
341
342
        $this->validateContainerName($name);
343
344
        try {
345
            $this->makeRequest(Client::DELETE, array($name));
346
        } catch (Exceptions\ResponseError $e) {
347
            switch ($e->getCode()) {
348
                case 409:
349
                    throw new Exceptions\ContainerNotEmpty($name);
350
                    break;
0 ignored issues
show
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
351
                case 404:
352
                    throw new Exceptions\NoSuchContainer();
353
                    break;
0 ignored issues
show
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
354
                default:
355
                    throw $e;
356
                    break;
0 ignored issues
show
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
357
            }
358
        }
359
360
        if ($this->getCdnEnabled()) {
361
            $this->makeCdnRequest(
362
                Client::POST,
363
                array($name),
364
                array(
365
                    'X-CDN-Enabled' => 'False',
366
                )
367
            );
368
        }
369
    }
370
371
    /**
372
     * Return container object.
373
     *
374
     * @param  string                                                $name
375
     * @return \OpenStackStorage\Container
376
     * @throws \OpenStackStorage\Exceptions\NoSuchContainer
377
     * @throws \Exception|\OpenStackStorage\Exceptions\ResponseError
378
     */
379
    public function getContainer($name)
380
    {
381
        $this->validateContainerName($name);
382
383
        try {
384
            $response = $this->makeRequest(Client::HEAD, array($name));
385
        } catch (Exceptions\ResponseError $e) {
386
            if (404 == $e->getCode()) {
387
                throw new Exceptions\NoSuchContainer();
388
            }
389
390
            throw $e;
391
        }
392
393
        $nbObjects = $response['headers']['x-container-object-count'];
394
        $sizeUsed  = $response['headers']['x-container-bytes-used'];
395
        $metadata  = array();
396
397
        foreach ($response['headers'] as $k => $value) {
0 ignored issues
show
The expression $response['headers'] of type string is not traversable.
Loading history...
398
            if (0 === strpos($k, 'x-container-meta-')) {
399
                $metadata[substr($k, 17)] = $value;
400
            }
401
        }
402
403
        return new Container($this, $name, $nbObjects, $sizeUsed, $metadata);
404
    }
405
406
    /**
407
     * Return array with containers.
408
     *
409
     * @param  array                         $parameters
410
     * @return \OpenStackStorage\Container[]
411
     */
412
    public function getContainers(array $parameters = array())
413
    {
414
        $result = array();
415
416
        foreach ($this->getContainersInfo($parameters) as $info) {
0 ignored issues
show
The expression $this->getContainersInfo($parameters) of type string is not traversable.
Loading history...
417
            $result[] = new Container($this, $info['name'], $info['count'], $info['bytes']);
418
        }
419
420
        return $result;
421
    }
422
423
    /**
424
     * Return names of public containers.
425
     *
426
     * @return array
427
     * @throws \OpenStackStorage\Exceptions\CDNNotEnabled
428
     */
429
    public function getPublicContainersList()
430
    {
431
        if (!$this->getCdnEnabled()) {
432
            throw new Exceptions\CDNNotEnabled();
433
        }
434
435
        $response = $this->makeCdnRequest(Client::GET);
436
437
        return explode("\n", trim($response['body']));
438
    }
439
440
    /**
441
     * Return information about containers.
442
     *
443
     * @see \OpenStackStorage\Connection::$allowedParameters
444
     * @param  array $parameters
445
     * @return array
446
     */
447
    public function getContainersInfo(array $parameters = array())
448
    {
449
        $parameters['format'] = 'json';
450
451
        return $this->getContainersRawData($parameters);
452
    }
453
454
    /**
455
     * Return names of containers.
456
     *
457
     * @see \OpenStackStorage\Connection::$allowedParameters
458
     * @param  array $parameters
459
     * @return array
460
     */
461
    public function getContainersList(array $parameters = array())
462
    {
463
        $parameters['format'] = 'plain';
464
465
        return explode("\n", trim($this->getContainersRawData($parameters)));
466
    }
467
468
    /**
469
     * Generate path for query string.
470
     *
471
     * @param  array  $path
472
     * @return string
473
     */
474
    protected function getPathFromArray(array $path = array())
475
    {
476
        $tmp = array();
477
478
        foreach ($path as $value) {
479
            $tmp[] = rawurlencode($value);
480
        }
481
482
        return sprintf(
483
            '/%s/%s',
484
            rtrim($this->connectionUrlInfo['path'], '/'),
485
            str_replace('%2F', '/', implode('/', $tmp))
486
        );
487
    }
488
489
    /**
490
     * Authenticate and setup this instance with the values returned.
491
     */
492
    protected function authenticate()
493
    {
494
        if (!$this->isAuthenticated) {
495
            list($url, $this->cdnUrl, $this->authToken) = $this->auth->authenticate();
496
            if ($this->useServicenet) {
497
                $url = str_replace('https://', 'https://snet-%s', $url);
498
            }
499
500
            $this->connectionUrlInfo = Utils::parseUrl($url);
501
            $this->httpConnect();
502
503
            if ($this->cdnUrl) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->cdnUrl of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
504
                $this->cdnConnect();
505
            }
506
507
            $this->isAuthenticated = true;
508
        }
509
    }
510
511
    /**
512
     * Setup the http connection instance.
513
     */
514
    protected function httpConnect()
515
    {
516
        $this->client = new Client(array('timeout' => $this->timeout));
517
        $this->client->setUserAgent($this->userAgent);
518
        $this->client->setBaseURL(sprintf(
519
            '%s://%s:%d',
520
            $this->connectionUrlInfo['scheme'],
521
            $this->connectionUrlInfo['host'],
522
            $this->connectionUrlInfo['port']
523
        ));
524
    }
525
526
    /**
527
     * Setup the http connection instance for the CDN service.
528
     */
529
    protected function cdnConnect()
530
    {
531
        $info             = Utils::parseUrl($this->cdnUrl);
532
        $this->cdnEnabled = true;
533
        $this->cdnClient  = new Client(array('timeout' => $this->timeout));
534
        $this->cdnClient->setUserAgent($this->userAgent);
535
        $this->cdnClient->setBaseURL(sprintf(
536
            '%s://%s:%d',
537
            $info['scheme'],
538
            $info['host'],
539
            $info['port']
540
        ));
541
    }
542
543
    /**
544
     * Performs the real http request.
545
     *
546
     * @param  \OpenStackStorage\Client $client
547
     * @param  string                   $method
548
     * @param  string                   $path
549
     * @param  array                    $parameters
550
     * @param  array                    $headers
551
     * @return array
552
     * @throws \Exception
553
     */
554
    protected function makeRealRequest(Client $client, $method, $path, $parameters = array(), array $headers = array())
555
    {
556
        $headers['X-Auth-Token'] = $this->authToken;
557
558
        return $client->sendRequest($path, $method, $parameters, $headers);
559
    }
560
561
    /**
562
     * Validates the container name.
563
     *
564
     * @param  string                                            $name
565
     * @throws \OpenStackStorage\Exceptions\InvalidContainerName
566
     */
567
    protected function validateContainerName($name)
568
    {
569
        if (empty($name)
570
            || (false !== strpos($name, '/'))
571
            || strlen($name) > CONTAINER_NAME_LIMIT) {
572
            throw new Exceptions\InvalidContainerName();
573
        }
574
    }
575
576
    /**
577
     * Return a raw response string with containers data.
578
     *
579
     * @see \OpenStackStorage\Connection::$allowedParameters
580
     * @param  array  $parameters
581
     * @return string
582
     */
583
    protected function getContainersRawData(array $parameters = array())
584
    {
585
        $cacheKey = md5(json_encode($parameters));
586
587 View Code Duplication
        if (!array_key_exists($cacheKey, self::$listContainersCache)) {
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...
588
            $tmp = array();
589
590
            foreach ($parameters as $k => $v) {
591
                if (in_array($k, self::$allowedParameters)) {
592
                    $tmp[$k] = $v;
593
                }
594
            }
595
596
            $response = $this->makeRequest(Client::GET, array(), array(), $tmp);
597
            self::$listContainersCache[$cacheKey] = $response['body'];
598
        }
599
600
        return self::$listContainersCache[$cacheKey];
601
    }
602
}
603