Issues (850)

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 mixed[]|null
31
     * @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
32
     */
33
    private static $installed;
34
35
    /**
36
     * @var bool|null
37
     */
38
    private static $canGetVendors;
39
40
    /**
41
     * @var array[]
42
     * @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[]}>}>
43
     */
44
    private static $installedByVendor = array();
45
46
    /**
47
     * Returns a list of all package names which are present, either by being installed, replaced or provided
48
     *
49
     * @return string[]
50
     * @psalm-return list<string>
51
     */
52
    public static function getInstalledPackages()
53
    {
54
        $packages = array();
55
        foreach (self::getInstalled() as $installed) {
56
            $packages[] = array_keys($installed['versions']);
57
        }
58
59
        if (1 === \count($packages)) {
60
            return $packages[0];
61
        }
62
63
        return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
64
    }
65
66
    /**
67
     * Returns a list of all package names with a specific type e.g. 'library'
68
     *
69
     * @param  string   $type
70
     * @return string[]
71
     * @psalm-return list<string>
72
     */
73
    public static function getInstalledPackagesByType($type)
74
    {
75
        $packagesByType = array();
76
77
        foreach (self::getInstalled() as $installed) {
78
            foreach ($installed['versions'] as $name => $package) {
79
                if (isset($package['type']) && $package['type'] === $type) {
80
                    $packagesByType[] = $name;
81
                }
82
            }
83
        }
84
85
        return $packagesByType;
86
    }
87
88
    /**
89
     * Checks whether the given package is installed
90
     *
91
     * This also returns true if the package name is provided or replaced by another package
92
     *
93
     * @param  string $packageName
94
     * @param  bool   $includeDevRequirements
95
     * @return bool
96
     */
97
    public static function isInstalled($packageName, $includeDevRequirements = true)
98
    {
99
        foreach (self::getInstalled() as $installed) {
100
            if (isset($installed['versions'][$packageName])) {
101
                return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
102
            }
103
        }
104
105
        return false;
106
    }
107
108
    /**
109
     * Checks whether the given package satisfies a version constraint
110
     *
111
     * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
112
     *
113
     *   Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
114
     *
115
     * @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...
116
     * @param  string        $packageName
117
     * @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
118
     * @return bool
119
     */
120
    public static function satisfies(VersionParser $parser, $packageName, $constraint)
121
    {
122
        $constraint = $parser->parseConstraints((string) $constraint);
123
        $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
124
125
        return $provided->matches($constraint);
126
    }
127
128
    /**
129
     * Returns a version constraint representing all the range(s) which are installed for a given package
130
     *
131
     * It is easier to use this via isInstalled() with the $constraint argument if you need to check
132
     * whether a given version of a package is installed, and not just whether it exists
133
     *
134
     * @param  string $packageName
135
     * @return string Version constraint usable with composer/semver
136
     */
137
    public static function getVersionRanges($packageName)
138
    {
139
        foreach (self::getInstalled() as $installed) {
140
            if (!isset($installed['versions'][$packageName])) {
141
                continue;
142
            }
143
144
            $ranges = array();
145
            if (isset($installed['versions'][$packageName]['pretty_version'])) {
146
                $ranges[] = $installed['versions'][$packageName]['pretty_version'];
147
            }
148
            if (array_key_exists('aliases', $installed['versions'][$packageName])) {
149
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
150
            }
151
            if (array_key_exists('replaced', $installed['versions'][$packageName])) {
152
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
153
            }
154
            if (array_key_exists('provided', $installed['versions'][$packageName])) {
155
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
156
            }
157
158
            return implode(' || ', $ranges);
159
        }
160
161
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
162
    }
163
164
    /**
165
     * @param  string      $packageName
166
     * @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
167
     */
168
    public static function getVersion($packageName)
169
    {
170
        foreach (self::getInstalled() as $installed) {
171
            if (!isset($installed['versions'][$packageName])) {
172
                continue;
173
            }
174
175
            if (!isset($installed['versions'][$packageName]['version'])) {
176
                return null;
177
            }
178
179
            return $installed['versions'][$packageName]['version'];
180
        }
181
182
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
183
    }
184
185
    /**
186
     * @param  string      $packageName
187
     * @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
188
     */
189
    public static function getPrettyVersion($packageName)
190
    {
191
        foreach (self::getInstalled() as $installed) {
192
            if (!isset($installed['versions'][$packageName])) {
193
                continue;
194
            }
195
196
            if (!isset($installed['versions'][$packageName]['pretty_version'])) {
197
                return null;
198
            }
199
200
            return $installed['versions'][$packageName]['pretty_version'];
201
        }
202
203
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
204
    }
205
206
    /**
207
     * @param  string      $packageName
208
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
209
     */
210
    public static function getReference($packageName)
211
    {
212
        foreach (self::getInstalled() as $installed) {
213
            if (!isset($installed['versions'][$packageName])) {
214
                continue;
215
            }
216
217
            if (!isset($installed['versions'][$packageName]['reference'])) {
218
                return null;
219
            }
220
221
            return $installed['versions'][$packageName]['reference'];
222
        }
223
224
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
225
    }
226
227
    /**
228
     * @param  string      $packageName
229
     * @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.
230
     */
231
    public static function getInstallPath($packageName)
232
    {
233
        foreach (self::getInstalled() as $installed) {
234
            if (!isset($installed['versions'][$packageName])) {
235
                continue;
236
            }
237
238
            return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
239
        }
240
241
        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
242
    }
243
244
    /**
245
     * @return array
246
     * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
247
     */
248
    public static function getRootPackage()
249
    {
250
        $installed = self::getInstalled();
251
252
        return $installed[0]['root'];
253
    }
254
255
    /**
256
     * Returns the raw installed.php data for custom implementations
257
     *
258
     * @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.
259
     * @return array[]
260
     * @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[]}>}
261
     */
262
    public static function getRawData()
263
    {
264
        @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);
265
266
        if (null === self::$installed) {
267
            // only require the installed.php file if this file is loaded from its dumped location,
268
            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
269
            if (substr(__DIR__, -8, 1) !== 'C') {
270
                self::$installed = include __DIR__ . '/installed.php';
271
            } else {
272
                self::$installed = array();
273
            }
274
        }
275
276
        return self::$installed;
277
    }
278
279
    /**
280
     * Returns the raw data of all installed.php which are currently loaded for custom implementations
281
     *
282
     * @return array[]
283
     * @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[]}>}>
284
     */
285
    public static function getAllRawData()
286
    {
287
        return self::getInstalled();
288
    }
289
290
    /**
291
     * Lets you reload the static array from another file
292
     *
293
     * This is only useful for complex integrations in which a project needs to use
294
     * this class but then also needs to execute another project's autoloader in process,
295
     * and wants to ensure both projects have access to their version of installed.php.
296
     *
297
     * A typical case would be PHPUnit, where it would need to make sure it reads all
298
     * the data it needs from this class, then call reload() with
299
     * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
300
     * the project in which it runs can then also use this class safely, without
301
     * interference between PHPUnit's dependencies and the project's dependencies.
302
     *
303
     * @param  array[] $data A vendor/composer/installed.php data set
304
     * @return void
305
     *
306
     * @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
307
     */
308
    public static function reload($data)
309
    {
310
        self::$installed = $data;
311
        self::$installedByVendor = array();
312
    }
313
314
    /**
315
     * @return array[]
316
     * @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[]}>}>
317
     */
318
    private static function getInstalled()
319
    {
320
        if (null === self::$canGetVendors) {
321
            self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
322
        }
323
324
        $installed = array();
325
326
        if (self::$canGetVendors) {
327
            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

327
            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...
328
                if (isset(self::$installedByVendor[$vendorDir])) {
329
                    $installed[] = self::$installedByVendor[$vendorDir];
330
                } elseif (is_file($vendorDir.'/composer/installed.php')) {
331
                    /** @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 */
332
                    $required = require $vendorDir.'/composer/installed.php';
333
                    $installed[] = self::$installedByVendor[$vendorDir] = $required;
334
                    if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
335
                        self::$installed = $installed[count($installed) - 1];
336
                    }
337
                }
338
            }
339
        }
340
341
        if (null === self::$installed) {
342
            // only require the installed.php file if this file is loaded from its dumped location,
343
            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
344
            if (substr(__DIR__, -8, 1) !== 'C') {
345
                /** @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 */
346
                $required = require __DIR__ . '/installed.php';
347
                self::$installed = $required;
348
            } else {
349
                self::$installed = array();
350
            }
351
        }
352
353
        if (self::$installed !== array()) {
354
            $installed[] = self::$installed;
355
        }
356
357
        return $installed;
358
    }
359
}
360