Issues (155)

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/Bowerphp/Bowerphp.php (6 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 is part of Bowerphp.
5
 *
6
 * (c) Massimiliano Arione <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Bowerphp;
13
14
use Bowerphp\Config\ConfigInterface;
15
use Bowerphp\Installer\InstallerInterface;
16
use Bowerphp\Output\BowerphpConsoleOutput;
17
use Bowerphp\Package\Package;
18
use Bowerphp\Package\PackageInterface;
19
use Bowerphp\Repository\RepositoryInterface;
20
use Bowerphp\Util\Filesystem;
21
use Github\Client;
22
use Guzzle\Http\Exception\RequestException;
23
use InvalidArgumentException;
24
use RuntimeException;
25
use Symfony\Component\Finder\Finder;
26
use vierbergenlars\SemVer\expression;
27
use vierbergenlars\SemVer\version;
28
29
/**
30
 * Main class
31
 */
32
class Bowerphp
0 ignored issues
show
This class has a complexity of 70 which exceeds the configured maximum of 50.

The class complexity is the sum of the complexity of all methods. A very high value is usually an indication that your class does not follow the single reponsibility principle and does more than one job.

Some resources for further reading:

You can also find more detailed suggestions for refactoring in the “Code” section of your repository.

Loading history...
The class Bowerphp has a coupling between objects value of 15. Consider to reduce the number of dependencies under 13.
Loading history...
33
{
34
    protected $config;
35
36
    protected $filesystem;
37
38
    protected $githubClient;
39
40
    protected $repository;
41
42
    protected $output;
43
44
    /**
45
     * @param ConfigInterface       $config
46
     * @param Filesystem            $filesystem
47
     * @param Client                $githubClient
48
     * @param RepositoryInterface   $repository
49
     * @param BowerphpConsoleOutput $output
50
     */
51
    public function __construct(
52
        ConfigInterface $config,
53
        Filesystem $filesystem,
54
        Client $githubClient,
55
        RepositoryInterface $repository,
56
        BowerphpConsoleOutput $output
57
    ) {
58
        $this->config = $config;
59
        $this->filesystem = $filesystem;
60
        $this->githubClient = $githubClient;
61
        $this->repository = $repository;
62
        $this->output = $output;
63
    }
64
65
    /**
66
     * Init bower.json
67
     *
68
     * @param array $params
69
     */
70
    public function init(array $params)
71
    {
72
        if ($this->config->bowerFileExists()) {
73
            $bowerJson = $this->config->getBowerFileContent();
74
            $this->config->setSaveToBowerJsonFile();
75
            $this->config->updateBowerJsonFile2($bowerJson, $params);
76
        } else {
77
            $this->config->initBowerJsonFile($params);
78
        }
79
    }
80
81
    /**
82
     * Install a single package
83
     *
84
     * @param PackageInterface   $package
85
     * @param InstallerInterface $installer
86
     * @param bool               $isDependency
87
     */
88
    public function installPackage(PackageInterface $package, InstallerInterface $installer, $isDependency = false)
89
    {
90
        if (false !== strpos($package->getName(), 'github')) {
91
            // install from a github endpoint
92
            $name = basename($package->getName(), '.git');
93
            $repoUrl = $package->getName();
94
            $package = new Package($name, $package->getRequiredVersion());
95
            $this->repository->setUrl($repoUrl)->setHttpClient($this->githubClient);
96
            $package->setRepository($this->repository);
97
            $packageTag = $this->repository->findPackage($package->getRequiredVersion());
98
            if (is_null($packageTag)) {
99
                throw new RuntimeException(sprintf('Cannot find package %s version %s.', $package->getName(), $package->getRequiredVersion()));
100
            }
101
        } else {
102
            $packageTag = $this->getPackageTag($package, true);
103
            $package->setRepository($this->repository);
104
        }
105
106
        $package->setVersion($packageTag);
107
108
        $this->updateBowerFile($package, $isDependency);
109
110
        // if package is already installed, match current version with latest available version
111
        if ($this->isPackageInstalled($package)) {
112
            $this->output->writelnInfoPackage($package, 'validate', sprintf('%s against %s#%s', $packageTag, $package->getName(), $package->getRequiredVersion()));
113
            $packageBower = $this->config->getPackageBowerFileContent($package);
114
            if ($packageTag == $packageBower['version']) {
115
                // if version is fully matching, there's no need to install
116
                return;
117
            }
118
        }
119
120
        $this->cachePackage($package);
121
122
        $this->output->writelnInstalledPackage($package);
123
        $installer->install($package);
124
125
        $overrides = $this->config->getOverrideFor($package->getName());
126
        if (array_key_exists('dependencies', $overrides)) {
127
            $dependencies = $overrides['dependencies'];
128
        } else {
129
            $dependencies = $package->getRequires();
130
        }
131
        if (!empty($dependencies)) {
132
            foreach ($dependencies as $name => $version) {
133
                $depPackage = new Package($name, $version);
134
                if (!$this->isPackageInstalled($depPackage)) {
135
                    $this->installPackage($depPackage, $installer, true);
136
                } elseif ($this->isNeedUpdate($depPackage)) {
137
                    $this->updatePackage($depPackage, $installer);
138
                }
139
            }
140
        }
141
    }
142
143
    /**
144
     * Install all dependencies
145
     *
146
     * @param InstallerInterface $installer
147
     */
148
    public function installDependencies(InstallerInterface $installer)
149
    {
150
        $decode = $this->config->getBowerFileContent();
151
        if (!empty($decode['dependencies'])) {
152
            foreach ($decode['dependencies'] as $name => $requiredVersion) {
153
                if (false !== strpos($requiredVersion, 'github')) {
154
                    list($name, $requiredVersion) = explode('#', $requiredVersion);
155
                }
156
                $package = new Package($name, $requiredVersion);
157
                $this->installPackage($package, $installer, true);
158
            }
159
        }
160
    }
161
162
    /**
163
     * Update a single package
164
     *
165
     * @param PackageInterface   $package
166
     * @param InstallerInterface $installer
167
     */
168
    public function updatePackage(PackageInterface $package, InstallerInterface $installer)
0 ignored issues
show
This operation has 800 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

You can also find more information in the “Code” section of your repository.

Loading history...
169
    {
170
        if (!$this->isPackageInstalled($package)) {
171
            throw new RuntimeException(sprintf('Package %s is not installed.', $package->getName()));
172
        }
173
        if (is_null($package->getRequiredVersion())) {
174
            $decode = $this->config->getBowerFileContent();
175
            if (empty($decode['dependencies']) || empty($decode['dependencies'][$package->getName()])) {
176
                throw new InvalidArgumentException(sprintf('Package %s not found in bower.json', $package->getName()));
177
            }
178
            $package->setRequiredVersion($decode['dependencies'][$package->getName()]);
179
        }
180
181
        $bower = $this->config->getPackageBowerFileContent($package);
182
        $package->setInfo($bower);
183
        $package->setVersion($bower['version']);
184
        $package->setRequires(isset($bower['dependencies']) ? $bower['dependencies'] : null);
185
186
        $packageTag = $this->getPackageTag($package);
187
        $this->output->writelnInfoPackage($package, 'validate', sprintf('%s against %s#%s', $packageTag, $package->getName(), $package->getRequiredVersion()));
188
189
        $package->setRepository($this->repository);
190
        if ($packageTag == $package->getVersion()) {
191
            // if version is fully matching, there's no need to update
192
            return;
193
        }
194
        $package->setVersion($packageTag);
195
196
        $this->cachePackage($package);
197
198
        $this->output->writelnUpdatingPackage($package);
199
        $installer->update($package);
200
201
        $overrides = $this->config->getOverrideFor($package->getName());
202
        if (array_key_exists('dependencies', $overrides)) {
203
            $dependencies = $overrides['dependencies'];
204
        } else {
205
            $dependencies = $package->getRequires();
206
        }
207
        if (!empty($dependencies)) {
208
            foreach ($dependencies as $name => $requiredVersion) {
209
                $depPackage = new Package($name, $requiredVersion);
210
                if (!$this->isPackageInstalled($depPackage)) {
211
                    $this->installPackage($depPackage, $installer, true);
212
                } elseif ($this->isNeedUpdate($depPackage)) {
213
                    $this->updatePackage($depPackage, $installer);
214
                }
215
            }
216
        }
217
    }
218
219
    /**
220
     * Update all dependencies
221
     *
222
     * @param InstallerInterface $installer
223
     */
224
    public function updatePackages(InstallerInterface $installer)
225
    {
226
        $decode = $this->config->getBowerFileContent();
227
        if (!empty($decode['dependencies'])) {
228
            foreach ($decode['dependencies'] as $packageName => $requiredVersion) {
229
                $this->updatePackage(new Package($packageName, $requiredVersion), $installer);
230
            }
231
        }
232
    }
233
234
    /**
235
     * @param  PackageInterface $package
236
     * @param  string           $info
237
     * @return mixed
238
     */
239
    public function getPackageInfo(PackageInterface $package, $info = 'url')
240
    {
241
        $decode = $this->lookupPackage($package->getName());
242
243
        $this->repository->setHttpClient($this->githubClient);
244
245
        if ('url' == $info) {
246
            $this->repository->setUrl($decode['url'], false);
247
248
            return $this->repository->getUrl();
249
        }
250
251
        if ('versions' == $info) {
252
            $tags = $this->repository->getTags();
253
            usort($tags, function ($a, $b) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $a. Configured minimum length is 2.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $b. Configured minimum length is 2.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
254
                return version_compare($b, $a);
255
            });
256
257
            return $tags;
258
        }
259
260
        throw new RuntimeException(sprintf('Unsupported info option "%s".', $info));
261
    }
262
263
    /**
264
     * @param  string $name
265
     * @return array
266
     */
267
    public function lookupPackage($name)
268
    {
269
        return $this->findPackage($name);
270
    }
271
272
    /**
273
     * @param  PackageInterface $package
274
     * @return string
275
     */
276
    public function getPackageBowerFile(PackageInterface $package)
277
    {
278
        $this->repository->setHttpClient($this->githubClient);
279
        $lookupPackage = $this->lookupPackage($package->getName());
280
        $this->repository->setUrl($lookupPackage['url'], false);
281
        $tag = $this->repository->findPackage($package->getRequiredVersion());
282
283
        return $this->repository->getBower($tag, true, $lookupPackage['url']);
284
    }
285
286
    /**
287
     * Uninstall a single package
288
     *
289
     * @param PackageInterface   $package
290
     * @param InstallerInterface $installer
291
     */
292
    public function uninstallPackage(PackageInterface $package, InstallerInterface $installer)
293
    {
294
        if (!$this->isPackageInstalled($package)) {
295
            throw new RuntimeException(sprintf('Package %s is not installed.', $package->getName()));
296
        }
297
        $installer->uninstall($package);
298
    }
299
300
    /**
301
     * Search packages by name
302
     *
303
     * @param  string $name
304
     * @return array
305
     */
306
    public function searchPackages($name)
307
    {
308
        try {
309
            $url = $this->config->getBasePackagesUrl() . 'search/' . $name;
310
            $response = $this->githubClient->getHttpClient()->get($url);
311
312
            return json_decode($response->getBody(true), true);
313
        } catch (RequestException $e) {
314
            throw new RuntimeException(sprintf('Cannot get package list from %s.', str_replace('/packages/', '', $this->config->getBasePackagesUrl())));
315
        }
316
    }
317
318
    /**
319
     * Get a list of installed packages
320
     *
321
     * @param  InstallerInterface $installer
322
     * @param  Finder             $finder
323
     * @return array
324
     */
325
    public function getInstalledPackages(InstallerInterface $installer, Finder $finder)
326
    {
327
        return $installer->getInstalled($finder);
328
    }
329
330
    /**
331
     * Check if package is installed
332
     *
333
     * @param  PackageInterface $package
334
     * @return bool
335
     */
336
    public function isPackageInstalled(PackageInterface $package)
337
    {
338
        return $this->filesystem->exists($this->config->getInstallDir() . '/' . $package->getName() . '/.bower.json');
339
    }
340
341
    /**
342
     * @param PackageInterface $package
343
     * @param bool             $checkInstall
344
     *
345
     * @return bool
346
     */
347
    public function isPackageExtraneous(PackageInterface $package, $checkInstall = false)
0 ignored issues
show
This operation has 216 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

You can also find more information in the “Code” section of your repository.

Loading history...
348
    {
349
        if ($checkInstall && !$this->isPackageInstalled($package)) {
350
            return false;
351
        }
352
353
        try {
354
            $bower = $this->config->getBowerFileContent();
355
        } catch (RuntimeException $e) { // no bower.json file, package is extraneous
356
            return true;
357
        }
358
        if (!isset($bower['dependencies'])) {
359
            return true;
360
        }
361
        // package is a direct dependencies
362
        if (isset($bower['dependencies'][$package->getName()])) {
363
            return false;
364
        }
365
        // look for dependencies of dependencies
366
        foreach ($bower['dependencies'] as $name => $version) {
367
            $dotBowerJson = $this->filesystem->read($this->config->getInstallDir() . '/' . $name . '/.bower.json');
368
            $depBower = json_decode($dotBowerJson, true);
369
            if (isset($depBower['dependencies'][$package->getName()])) {
370
                return false;
371
            }
372
            // look for dependencies of dependencies of dependencies
373
            if (isset($depBower['dependencies'])) {
374
                foreach ($depBower['dependencies'] as $name1 => $version1) {
375
                    $dotBowerJson = $this->filesystem->read($this->config->getInstallDir() . '/' . $name1 . '/.bower.json');
376
                    $depDepBower = json_decode($dotBowerJson, true);
377
                    if (isset($depDepBower['dependencies'][$package->getName()])) {
378
                        return false;
379
                    }
380
                }
381
            }
382
        }
383
384
        return true;
385
    }
386
387
    /**
388
     * @param  array $params
389
     * @return array
390
     */
391
    protected function createAClearBowerFile(array $params)
392
    {
393
        $authors = ['Beelab <[email protected]>'];
394
        if (!empty($params['author'])) {
395
            $authors[] = $params['author'];
396
        }
397
        $structure = [
398
            'name'         => $params['name'],
399
            'authors'      => $authors,
400
            'private'      => true,
401
            'dependencies' => new \StdClass(),
402
        ];
403
404
        return $structure;
405
    }
406
407
    /**
408
     * @param  PackageInterface $package
409
     * @param  bool             $setInfo
410
     * @return string
411
     */
412
    protected function getPackageTag(PackageInterface $package, $setInfo = false)
413
    {
414
        $decode = $this->findPackage($package->getName());
415
        // open package repository
416
        $repoUrl = $decode['url'];
417
        $this->repository->setUrl($repoUrl)->setHttpClient($this->githubClient);
418
        $packageTag = $this->repository->findPackage($package->getRequiredVersion());
419
        if (is_null($packageTag)) {
420
            throw new RuntimeException(sprintf('Cannot find package %s version %s.', $package->getName(), $package->getRequiredVersion()));
421
        }
422
        $bowerJson = $this->repository->getBower($packageTag);
423
        $bower = json_decode($bowerJson, true);
424
        if (!is_array($bower)) {
425
            throw new RuntimeException(sprintf('Invalid bower.json found in package %s: %s.', $package->getName(), $bowerJson));
426
        }
427
        if ($setInfo) {
428
            $package->setInfo($bower);
429
        }
430
431
        return $packageTag;
432
    }
433
434
    /**
435
     * @param  string $name
436
     * @return array
437
     */
438
    protected function findPackage($name)
439
    {
440
        try {
441
            $response = $this->githubClient->getHttpClient()->get($this->config->getBasePackagesUrl() . urlencode($name));
442
        } catch (RuntimeException $e) {
443
            throw new RuntimeException(sprintf('Cannot fetch registry info for package %s from search registry (%s).', $name, $e->getMessage()));
444
        }
445
        $packageInfo = json_decode($response->getBody(true), true);
446
        if (!is_array($packageInfo) || empty($packageInfo['url'])) {
447
            throw new RuntimeException(sprintf('Registry info for package %s has malformed json or is missing "url".', $name));
448
        }
449
450
        return $packageInfo;
451
    }
452
453
    /**
454
     * @param PackageInterface $package
455
     */
456
    private function cachePackage(PackageInterface $package)
457
    {
458
        $this->output->writelnInfoPackage($package, 'download');
459
460
        // get release archive from repository
461
        $file = $this->repository->getRelease();
462
463
        $tmpFileName = $this->config->getCacheDir() . '/tmp/' . $package->getName();
464
        $this->filesystem->write($tmpFileName, $file);
465
    }
466
467
    /**
468
     * @param PackageInterface $package
469
     * @param bool             $isDependency
470
     */
471
    private function updateBowerFile(PackageInterface $package, $isDependency = false)
472
    {
473
        if ($this->config->isSaveToBowerJsonFile() && !$isDependency) {
474
            try {
475
                $this->config->updateBowerJsonFile($package);
476
            } catch (RuntimeException $e) {
477
                $this->output->writelnNoBowerJsonFile();
478
            }
479
        }
480
    }
481
482
    /**
483
     * Update only if needed is greater version
484
     *
485
     * @param  PackageInterface $package
486
     * @return bool
487
     */
488
    public function isNeedUpdate($package)
489
    {
490
        $packageBower = $this->config->getPackageBowerFileContent($package);
491
        $semver = new version($packageBower['version']);
492
493
        return !$semver->satisfies(new expression($package->getRequiredVersion()));
494
    }
495
}
496