Issues (865)

Security Analysis    4 potential vulnerabilities

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  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.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  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.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  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.
  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.
  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.
  Code Injection (1)
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection (2)
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  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.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  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.
  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.
  Cross-Site Scripting (1)
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.
  Header Injection
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.

vendor/composer/InstalledVersions.php (2 issues)

Labels
Severity
1
<?php
2
3
/*
4
 * This file is part of Composer.
5
 *
6
 * (c) Nils Adermann <[email protected]>
7
 *     Jordi Boggiano <[email protected]>
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace Composer;
14
15
use Composer\Autoload\ClassLoader;
16
use Composer\Semver\VersionParser;
17
18
/**
19
 * This class is copied in every Composer installed project and available to all
20
 *
21
 * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
22
 *
23
 * To require its presence, you can require `composer-runtime-api ^2.0`
24
 *
25
 * @final
26
 */
27
class InstalledVersions
28
{
29
    /**
30
     * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
31
     * @internal
32
     */
33
    private static $selfDir = null;
34
35
    /**
36
     * @var mixed[]|null
37
     * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
38
     */
39
    private static $installed;
40
41
    /**
42
     * @var bool
43
     */
44
    private static $installedIsLocalDir;
45
46
    /**
47
     * @var bool|null
48
     */
49
    private static $canGetVendors;
50
51
    /**
52
     * @var array[]
53
     * @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
54
     */
55
    private static $installedByVendor = array();
56
57
    /**
58
     * Returns a list of all package names which are present, either by being installed, replaced or provided
59
     *
60
     * @return string[]
61
     * @psalm-return list<string>
62
     */
63
    public static function getInstalledPackages()
64
    {
65
        $packages = array();
66
        foreach (self::getInstalled() as $installed) {
67
            $packages[] = array_keys($installed['versions']);
68
        }
69
70
        if (1 === \count($packages)) {
71
            return $packages[0];
72
        }
73
74
        return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
75
    }
76
77
    /**
78
     * Returns a list of all package names with a specific type e.g. 'library'
79
     *
80
     * @param  string   $type
81
     * @return string[]
82
     * @psalm-return list<string>
83
     */
84
    public static function getInstalledPackagesByType($type)
85
    {
86
        $packagesByType = array();
87
88
        foreach (self::getInstalled() as $installed) {
89
            foreach ($installed['versions'] as $name => $package) {
90
                if (isset($package['type']) && $package['type'] === $type) {
91
                    $packagesByType[] = $name;
92
                }
93
            }
94
        }
95
96
        return $packagesByType;
97
    }
98
99
    /**
100
     * Checks whether the given package is installed
101
     *
102
     * This also returns true if the package name is provided or replaced by another package
103
     *
104
     * @param  string $packageName
105
     * @param  bool   $includeDevRequirements
106
     * @return bool
107
     */
108
    public static function isInstalled($packageName, $includeDevRequirements = true)
109
    {
110
        foreach (self::getInstalled() as $installed) {
111
            if (isset($installed['versions'][$packageName])) {
112
                return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
113
            }
114
        }
115
116
        return false;
117
    }
118
119
    /**
120
     * Checks whether the given package satisfies a version constraint
121
     *
122
     * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
123
     *
124
     *   Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
125
     *
126
     * @param  VersionParser $parser      Install composer/semver to have access to this class and functionality
0 ignored issues
show
The type Composer\Semver\VersionParser was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
127
     * @param  string        $packageName
128
     * @param  string|null   $constraint  A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
129
     * @return bool
130
     */
131
    public static function satisfies(VersionParser $parser, $packageName, $constraint)
132
    {
133
        $constraint = $parser->parseConstraints((string) $constraint);
134
        $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
135
136
        return $provided->matches($constraint);
137
    }
138
139
    /**
140
     * Returns a version constraint representing all the range(s) which are installed for a given package
141
     *
142
     * It is easier to use this via isInstalled() with the $constraint argument if you need to check
143
     * whether a given version of a package is installed, and not just whether it exists
144
     *
145
     * @param  string $packageName
146
     * @return string Version constraint usable with composer/semver
147
     */
148
    public static function getVersionRanges($packageName)
149
    {
150
        foreach (self::getInstalled() as $installed) {
151
            if (!isset($installed['versions'][$packageName])) {
152
                continue;
153
            }
154
155
            $ranges = array();
156
            if (isset($installed['versions'][$packageName]['pretty_version'])) {
157
                $ranges[] = $installed['versions'][$packageName]['pretty_version'];
158
            }
159
            if (array_key_exists('aliases', $installed['versions'][$packageName])) {
160
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
161
            }
162
            if (array_key_exists('replaced', $installed['versions'][$packageName])) {
163
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
164
            }
165
            if (array_key_exists('provided', $installed['versions'][$packageName])) {
166
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
167
            }
168
169
            return implode(' || ', $ranges);
170
        }
171
172
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
173
    }
174
175
    /**
176
     * @param  string      $packageName
177
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
178
     */
179
    public static function getVersion($packageName)
180
    {
181
        foreach (self::getInstalled() as $installed) {
182
            if (!isset($installed['versions'][$packageName])) {
183
                continue;
184
            }
185
186
            if (!isset($installed['versions'][$packageName]['version'])) {
187
                return null;
188
            }
189
190
            return $installed['versions'][$packageName]['version'];
191
        }
192
193
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
194
    }
195
196
    /**
197
     * @param  string      $packageName
198
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
199
     */
200
    public static function getPrettyVersion($packageName)
201
    {
202
        foreach (self::getInstalled() as $installed) {
203
            if (!isset($installed['versions'][$packageName])) {
204
                continue;
205
            }
206
207
            if (!isset($installed['versions'][$packageName]['pretty_version'])) {
208
                return null;
209
            }
210
211
            return $installed['versions'][$packageName]['pretty_version'];
212
        }
213
214
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
215
    }
216
217
    /**
218
     * @param  string      $packageName
219
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
220
     */
221
    public static function getReference($packageName)
222
    {
223
        foreach (self::getInstalled() as $installed) {
224
            if (!isset($installed['versions'][$packageName])) {
225
                continue;
226
            }
227
228
            if (!isset($installed['versions'][$packageName]['reference'])) {
229
                return null;
230
            }
231
232
            return $installed['versions'][$packageName]['reference'];
233
        }
234
235
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
236
    }
237
238
    /**
239
     * @param  string      $packageName
240
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
241
     */
242
    public static function getInstallPath($packageName)
243
    {
244
        foreach (self::getInstalled() as $installed) {
245
            if (!isset($installed['versions'][$packageName])) {
246
                continue;
247
            }
248
249
            return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
250
        }
251
252
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
253
    }
254
255
    /**
256
     * @return array
257
     * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
258
     */
259
    public static function getRootPackage()
260
    {
261
        $installed = self::getInstalled();
262
263
        return $installed[0]['root'];
264
    }
265
266
    /**
267
     * Returns the raw installed.php data for custom implementations
268
     *
269
     * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
270
     * @return array[]
271
     * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
272
     */
273
    public static function getRawData()
274
    {
275
        @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
276
277
        if (null === self::$installed) {
278
            // only require the installed.php file if this file is loaded from its dumped location,
279
            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
280
            if (substr(__DIR__, -8, 1) !== 'C') {
281
                self::$installed = include __DIR__ . '/installed.php';
282
            } else {
283
                self::$installed = array();
284
            }
285
        }
286
287
        return self::$installed;
288
    }
289
290
    /**
291
     * Returns the raw data of all installed.php which are currently loaded for custom implementations
292
     *
293
     * @return array[]
294
     * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
295
     */
296
    public static function getAllRawData()
297
    {
298
        return self::getInstalled();
299
    }
300
301
    /**
302
     * Lets you reload the static array from another file
303
     *
304
     * This is only useful for complex integrations in which a project needs to use
305
     * this class but then also needs to execute another project's autoloader in process,
306
     * and wants to ensure both projects have access to their version of installed.php.
307
     *
308
     * A typical case would be PHPUnit, where it would need to make sure it reads all
309
     * the data it needs from this class, then call reload() with
310
     * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
311
     * the project in which it runs can then also use this class safely, without
312
     * interference between PHPUnit's dependencies and the project's dependencies.
313
     *
314
     * @param  array[] $data A vendor/composer/installed.php data set
315
     * @return void
316
     *
317
     * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
318
     */
319
    public static function reload($data)
320
    {
321
        self::$installed = $data;
322
        self::$installedByVendor = array();
323
324
        // when using reload, we disable the duplicate protection to ensure that self::$installed data is
325
        // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
326
        // so we have to assume it does not, and that may result in duplicate data being returned when listing
327
        // all installed packages for example
328
        self::$installedIsLocalDir = false;
329
    }
330
331
    /**
332
     * @return string
333
     */
334
    private static function getSelfDir()
335
    {
336
        if (self::$selfDir === null) {
337
            self::$selfDir = strtr(__DIR__, '\\', '/');
338
        }
339
340
        return self::$selfDir;
341
    }
342
343
    /**
344
     * @return array[]
345
     * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
346
     */
347
    private static function getInstalled()
348
    {
349
        if (null === self::$canGetVendors) {
350
            self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
351
        }
352
353
        $installed = array();
354
        $copiedLocalDir = false;
355
356
        if (self::$canGetVendors) {
357
            $selfDir = self::getSelfDir();
358
            foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
0 ignored issues
show
The method getRegisteredLoaders() does not exist on Composer\Autoload\ClassLoader. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

358
            foreach (ClassLoader::/** @scrutinizer ignore-call */ getRegisteredLoaders() as $vendorDir => $loader) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
359
                $vendorDir = strtr($vendorDir, '\\', '/');
360
                if (isset(self::$installedByVendor[$vendorDir])) {
361
                    $installed[] = self::$installedByVendor[$vendorDir];
362
                } elseif (is_file($vendorDir.'/composer/installed.php')) {
363
                    /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
364
                    $required = require $vendorDir.'/composer/installed.php';
365
                    self::$installedByVendor[$vendorDir] = $required;
366
                    $installed[] = $required;
367
                    if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
368
                        self::$installed = $required;
369
                        self::$installedIsLocalDir = true;
370
                    }
371
                }
372
                if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
373
                    $copiedLocalDir = true;
374
                }
375
            }
376
        }
377
378
        if (null === self::$installed) {
379
            // only require the installed.php file if this file is loaded from its dumped location,
380
            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
381
            if (substr(__DIR__, -8, 1) !== 'C') {
382
                /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
383
                $required = require __DIR__ . '/installed.php';
384
                self::$installed = $required;
385
            } else {
386
                self::$installed = array();
387
            }
388
        }
389
390
        if (self::$installed !== array() && !$copiedLocalDir) {
391
            $installed[] = self::$installed;
392
        }
393
394
        return $installed;
395
    }
396
}
397