Skip to content

Commit c58ae28

Browse files
committed
Fix bundle auto-detection
1 parent d606fab commit c58ae28

File tree

14 files changed

+789
-3
lines changed

14 files changed

+789
-3
lines changed

src/SymfonyBundle.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function getClassNames(): array
4949
foreach ($this->extractClassNames($namespace, $isSyliusPlugin) as $class) {
5050
// we only check class existence on install as we do have the code available
5151
// in contrast to uninstall operation
52-
if (!$uninstall && !$this->checkClassExists($class, $path, $isPsr4)) {
52+
if (!$uninstall && !$this->isBundleClass($class, $path, $isPsr4)) {
5353
continue;
5454
}
5555

@@ -92,7 +92,7 @@ private function extractClassNames(string $namespace, bool $isSyliusPlugin): arr
9292
return array_unique($classes);
9393
}
9494

95-
private function checkClassExists(string $class, string $path, bool $isPsr4): bool
95+
private function isBundleClass(string $class, string $path, bool $isPsr4): bool
9696
{
9797
$classPath = ($this->vendorDir ? $this->vendorDir.'/' : '').$this->package->getPrettyName().'/'.$path.'/';
9898
$parts = explode('\\', $class);
@@ -102,6 +102,11 @@ private function checkClassExists(string $class, string $path, bool $isPsr4): bo
102102
}
103103
$classPath .= str_replace('\\', '/', $class).'.php';
104104

105-
return file_exists($classPath);
105+
if (!file_exists($classPath)) {
106+
return false;
107+
}
108+
109+
// heuristic that should work in almost all cases
110+
return false !== strpos(file_get_contents($classPath), 'use Symfony\Component\HttpKernel\Bundle\Bundle');
106111
}
107112
}
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
<?php
2+
3+
/*
4+
* This file is part of composer/ca-bundle.
5+
*
6+
* (c) Composer <https://github.com/composer>
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE file that was distributed with this source code.
10+
*/
11+
12+
namespace Composer\CaBundle;
13+
14+
use Psr\Log\LoggerInterface;
15+
use Symfony\Component\Process\PhpProcess;
16+
17+
/**
18+
* @author Chris Smith <[email protected]>
19+
* @author Jordi Boggiano <[email protected]>
20+
*/
21+
class CaBundle
22+
{
23+
private static $caPath;
24+
private static $caFileValidity = array();
25+
private static $useOpensslParse;
26+
27+
/**
28+
* Returns the system CA bundle path, or a path to the bundled one
29+
*
30+
* This method was adapted from Sslurp.
31+
* https://github.com/EvanDotPro/Sslurp
32+
*
33+
* (c) Evan Coury <[email protected]>
34+
*
35+
* For the full copyright and license information, please see below:
36+
*
37+
* Copyright (c) 2013, Evan Coury
38+
* All rights reserved.
39+
*
40+
* Redistribution and use in source and binary forms, with or without modification,
41+
* are permitted provided that the following conditions are met:
42+
*
43+
* * Redistributions of source code must retain the above copyright notice,
44+
* this list of conditions and the following disclaimer.
45+
*
46+
* * Redistributions in binary form must reproduce the above copyright notice,
47+
* this list of conditions and the following disclaimer in the documentation
48+
* and/or other materials provided with the distribution.
49+
*
50+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
51+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
52+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
53+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
54+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
55+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
56+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
57+
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
58+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
59+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60+
*
61+
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
62+
* @return string path to a CA bundle file or directory
63+
*/
64+
public static function getSystemCaRootBundlePath(LoggerInterface $logger = null)
65+
{
66+
if (self::$caPath !== null) {
67+
return self::$caPath;
68+
}
69+
$caBundlePaths = array();
70+
71+
72+
// If SSL_CERT_FILE env variable points to a valid certificate/bundle, use that.
73+
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
74+
$caBundlePaths[] = self::getEnvVariable('SSL_CERT_FILE');
75+
76+
// If SSL_CERT_DIR env variable points to a valid certificate/bundle, use that.
77+
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
78+
$caBundlePaths[] = self::getEnvVariable('SSL_CERT_DIR');
79+
80+
$caBundlePaths[] = ini_get('openssl.cafile');
81+
$caBundlePaths[] = ini_get('openssl.capath');
82+
83+
$otherLocations = array(
84+
'/etc/pki/tls/certs/ca-bundle.crt', // Fedora, RHEL, CentOS (ca-certificates package)
85+
'/etc/ssl/certs/ca-certificates.crt', // Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package)
86+
'/etc/ssl/ca-bundle.pem', // SUSE, openSUSE (ca-certificates package)
87+
'/usr/local/share/certs/ca-root-nss.crt', // FreeBSD (ca_root_nss_package)
88+
'/usr/ssl/certs/ca-bundle.crt', // Cygwin
89+
'/opt/local/share/curl/curl-ca-bundle.crt', // OS X macports, curl-ca-bundle package
90+
'/usr/local/share/curl/curl-ca-bundle.crt', // Default cURL CA bunde path (without --with-ca-bundle option)
91+
'/usr/share/ssl/certs/ca-bundle.crt', // Really old RedHat?
92+
'/etc/ssl/cert.pem', // OpenBSD
93+
'/usr/local/etc/ssl/cert.pem', // FreeBSD 10.x
94+
'/usr/local/etc/openssl/cert.pem', // OS X homebrew, openssl package
95+
'/usr/local/etc/[email protected]/cert.pem', // OS X homebrew, [email protected] package
96+
);
97+
98+
foreach($otherLocations as $location) {
99+
$otherLocations[] = dirname($location);
100+
}
101+
102+
$caBundlePaths = array_merge($caBundlePaths, $otherLocations);
103+
104+
foreach ($caBundlePaths as $caBundle) {
105+
if (self::caFileUsable($caBundle, $logger)) {
106+
return self::$caPath = $caBundle;
107+
}
108+
109+
if (self::caDirUsable($caBundle)) {
110+
return self::$caPath = $caBundle;
111+
}
112+
}
113+
114+
return self::$caPath = static::getBundledCaBundlePath(); // Bundled CA file, last resort
115+
}
116+
117+
/**
118+
* Returns the path to the bundled CA file
119+
*
120+
* In case you don't want to trust the user or the system, you can use this directly
121+
*
122+
* @return string path to a CA bundle file
123+
*/
124+
public static function getBundledCaBundlePath()
125+
{
126+
$caBundleFile = __DIR__.'/../res/cacert.pem';
127+
128+
// cURL does not understand 'phar://' paths
129+
// see https://github.com/composer/ca-bundle/issues/10
130+
if (0 === strpos($caBundleFile, 'phar://')) {
131+
file_put_contents(
132+
$tempCaBundleFile = tempnam(sys_get_temp_dir(), 'openssl-ca-bundle-'),
133+
file_get_contents($caBundleFile)
134+
);
135+
136+
register_shutdown_function(function() use ($tempCaBundleFile) {
137+
@unlink($tempCaBundleFile);
138+
});
139+
140+
$caBundleFile = $tempCaBundleFile;
141+
}
142+
143+
return $caBundleFile;
144+
}
145+
146+
/**
147+
* Validates a CA file using opensl_x509_parse only if it is safe to use
148+
*
149+
* @param string $filename
150+
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
151+
*
152+
* @return bool
153+
*/
154+
public static function validateCaFile($filename, LoggerInterface $logger = null)
155+
{
156+
static $warned = false;
157+
158+
if (isset(self::$caFileValidity[$filename])) {
159+
return self::$caFileValidity[$filename];
160+
}
161+
162+
$contents = file_get_contents($filename);
163+
164+
// assume the CA is valid if php is vulnerable to
165+
// https://www.sektioneins.de/advisories/advisory-012013-php-openssl_x509_parse-memory-corruption-vulnerability.html
166+
if (!static::isOpensslParseSafe()) {
167+
if (!$warned && $logger) {
168+
$logger->warning(sprintf(
169+
'Your version of PHP, %s, is affected by CVE-2013-6420 and cannot safely perform certificate validation, we strongly suggest you upgrade.',
170+
PHP_VERSION
171+
));
172+
$warned = true;
173+
}
174+
175+
$isValid = !empty($contents);
176+
} else {
177+
$contents = preg_replace("/^(\\-+(?:BEGIN|END))\\s+TRUSTED\\s+(CERTIFICATE\\-+)\$/m", '$1 $2', $contents);
178+
$isValid = (bool) openssl_x509_parse($contents);
179+
}
180+
181+
if ($logger) {
182+
$logger->debug('Checked CA file '.realpath($filename).': '.($isValid ? 'valid' : 'invalid'));
183+
}
184+
185+
return self::$caFileValidity[$filename] = $isValid;
186+
}
187+
188+
/**
189+
* Test if it is safe to use the PHP function openssl_x509_parse().
190+
*
191+
* This checks if OpenSSL extensions is vulnerable to remote code execution
192+
* via the exploit documented as CVE-2013-6420.
193+
*
194+
* @return bool
195+
*/
196+
public static function isOpensslParseSafe()
197+
{
198+
if (null !== self::$useOpensslParse) {
199+
return self::$useOpensslParse;
200+
}
201+
202+
if (PHP_VERSION_ID >= 50600) {
203+
return self::$useOpensslParse = true;
204+
}
205+
206+
// Vulnerable:
207+
// PHP 5.3.0 - PHP 5.3.27
208+
// PHP 5.4.0 - PHP 5.4.22
209+
// PHP 5.5.0 - PHP 5.5.6
210+
if (
211+
(PHP_VERSION_ID < 50400 && PHP_VERSION_ID >= 50328)
212+
|| (PHP_VERSION_ID < 50500 && PHP_VERSION_ID >= 50423)
213+
|| (PHP_VERSION_ID < 50600 && PHP_VERSION_ID >= 50507)
214+
) {
215+
// This version of PHP has the fix for CVE-2013-6420 applied.
216+
return self::$useOpensslParse = true;
217+
}
218+
219+
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
220+
// Windows is probably insecure in this case.
221+
return self::$useOpensslParse = false;
222+
}
223+
224+
$compareDistroVersionPrefix = function ($prefix, $fixedVersion) {
225+
$regex = '{^'.preg_quote($prefix).'([0-9]+)$}';
226+
227+
if (preg_match($regex, PHP_VERSION, $m)) {
228+
return ((int) $m[1]) >= $fixedVersion;
229+
}
230+
231+
return false;
232+
};
233+
234+
// Hard coded list of PHP distributions with the fix backported.
235+
if (
236+
$compareDistroVersionPrefix('5.3.3-7+squeeze', 18) // Debian 6 (Squeeze)
237+
|| $compareDistroVersionPrefix('5.4.4-14+deb7u', 7) // Debian 7 (Wheezy)
238+
|| $compareDistroVersionPrefix('5.3.10-1ubuntu3.', 9) // Ubuntu 12.04 (Precise)
239+
) {
240+
return self::$useOpensslParse = true;
241+
}
242+
243+
// Symfony Process component is missing so we assume it is unsafe at this point
244+
if (!class_exists('Symfony\Component\Process\PhpProcess')) {
245+
return self::$useOpensslParse = false;
246+
}
247+
248+
// This is where things get crazy, because distros backport security
249+
// fixes the chances are on NIX systems the fix has been applied but
250+
// it's not possible to verify that from the PHP version.
251+
//
252+
// To verify exec a new PHP process and run the issue testcase with
253+
// known safe input that replicates the bug.
254+
255+
// Based on testcase in https://github.com/php/php-src/commit/c1224573c773b6845e83505f717fbf820fc18415
256+
// changes in https://github.com/php/php-src/commit/76a7fd893b7d6101300cc656058704a73254d593
257+
$cert = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVwRENDQTR5Z0F3SUJBZ0lKQUp6dThyNnU2ZUJjTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUhETVFzd0NRWUQKVlFRR0V3SkVSVEVjTUJvR0ExVUVDQXdUVG05eVpISm9aV2x1TFZkbGMzUm1ZV3hsYmpFUU1BNEdBMVVFQnd3SApTOE9Ed3Jac2JqRVVNQklHQTFVRUNnd0xVMlZyZEdsdmJrVnBibk14SHpBZEJnTlZCQXNNRmsxaGJHbGphVzkxCmN5QkRaWEowSUZObFkzUnBiMjR4SVRBZkJnTlZCQU1NR0cxaGJHbGphVzkxY3k1elpXdDBhVzl1WldsdWN5NWsKWlRFcU1DZ0dDU3FHU0liM0RRRUpBUlliYzNSbFptRnVMbVZ6YzJWeVFITmxhM1JwYjI1bGFXNXpMbVJsTUhVWQpaREU1TnpBd01UQXhNREF3TURBd1dnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBCkFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFBQUFBQVhEVEUwTVRFeU9ERXhNemt6TlZvd2djTXhDekFKQmdOVkJBWVRBa1JGTVJ3d0dnWURWUVFJREJOTwpiM0prY21obGFXNHRWMlZ6ZEdaaGJHVnVNUkF3RGdZRFZRUUhEQWRMdzRQQ3RteHVNUlF3RWdZRFZRUUtEQXRUClpXdDBhVzl1UldsdWN6RWZNQjBHQTFVRUN3d1dUV0ZzYVdOcGIzVnpJRU5sY25RZ1UyVmpkR2x2YmpFaE1COEcKQTFVRUF3d1liV0ZzYVdOcGIzVnpMbk5sYTNScGIyNWxhVzV6TG1SbE1Tb3dLQVlKS29aSWh2Y05BUWtCRmh0egpkR1ZtWVc0dVpYTnpaWEpBYzJWcmRHbHZibVZwYm5NdVpHVXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRRERBZjNobDdKWTBYY0ZuaXlFSnBTU0RxbjBPcUJyNlFQNjV1c0pQUnQvOFBhRG9xQnUKd0VZVC9OYSs2ZnNnUGpDMHVLOURaZ1dnMnRIV1dvYW5TYmxBTW96NVBINlorUzRTSFJaN2UyZERJalBqZGhqaAowbUxnMlVNTzV5cDBWNzk3R2dzOWxOdDZKUmZIODFNTjJvYlhXczROdHp0TE11RDZlZ3FwcjhkRGJyMzRhT3M4CnBrZHVpNVVhd1Raa3N5NXBMUEhxNWNNaEZHbTA2djY1Q0xvMFYyUGQ5K0tBb2tQclBjTjVLTEtlYno3bUxwazYKU01lRVhPS1A0aWRFcXh5UTdPN2ZCdUhNZWRzUWh1K3ByWTNzaTNCVXlLZlF0UDVDWm5YMmJwMHdLSHhYMTJEWAoxbmZGSXQ5RGJHdkhUY3lPdU4rblpMUEJtM3ZXeG50eUlJdlZBZ01CQUFHalFqQkFNQWtHQTFVZEV3UUNNQUF3CkVRWUpZSVpJQVliNFFnRUJCQVFEQWdlQU1Bc0dBMVVkRHdRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUFHMGZaWVlDVGJkajFYWWMrMVNub2FQUit2SThDOENhRAo4KzBVWWhkbnlVNGdnYTBCQWNEclk5ZTk0ZUVBdTZacXljRjZGakxxWFhkQWJvcHBXb2NyNlQ2R0QxeDMzQ2tsClZBcnpHL0t4UW9oR0QySmVxa2hJTWxEb214SE83a2EzOStPYThpMnZXTFZ5alU4QVp2V01BcnVIYTRFRU55RzcKbFcyQWFnYUZLRkNyOVRuWFRmcmR4R1ZFYnY3S1ZRNmJkaGc1cDVTanBXSDErTXEwM3VSM1pYUEJZZHlWODMxOQpvMGxWajFLRkkyRENML2xpV2lzSlJvb2YrMWNSMzVDdGQwd1lCY3BCNlRac2xNY09QbDc2ZHdLd0pnZUpvMlFnClpzZm1jMnZDMS9xT2xOdU5xLzBUenprVkd2OEVUVDNDZ2FVK1VYZTRYT1Z2a2NjZWJKbjJkZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K';
258+
$script = <<<'EOT'
259+
260+
error_reporting(-1);
261+
$info = openssl_x509_parse(base64_decode('%s'));
262+
var_dump(PHP_VERSION, $info['issuer']['emailAddress'], $info['validFrom_time_t']);
263+
264+
EOT;
265+
$script = '<'."?php\n".sprintf($script, $cert);
266+
267+
try {
268+
$process = new PhpProcess($script);
269+
$process->mustRun();
270+
} catch (\Exception $e) {
271+
// In the case of any exceptions just accept it is not possible to
272+
// determine the safety of openssl_x509_parse and bail out.
273+
return self::$useOpensslParse = false;
274+
}
275+
276+
$output = preg_split('{\r?\n}', trim($process->getOutput()));
277+
$errorOutput = trim($process->getErrorOutput());
278+
279+
if (
280+
count($output) === 3
281+
&& $output[0] === sprintf('string(%d) "%s"', strlen(PHP_VERSION), PHP_VERSION)
282+
&& $output[1] === 'string(27) "[email protected]"'
283+
&& $output[2] === 'int(-1)'
284+
&& preg_match('{openssl_x509_parse\(\): illegal (?:ASN1 data type for|length in) timestamp in - on line \d+}', $errorOutput)
285+
) {
286+
// This PHP has the fix backported probably by a distro security team.
287+
return self::$useOpensslParse = true;
288+
}
289+
290+
return self::$useOpensslParse = false;
291+
}
292+
293+
/**
294+
* Resets the static caches
295+
*/
296+
public static function reset()
297+
{
298+
self::$caFileValidity = array();
299+
self::$caPath = null;
300+
self::$useOpensslParse = null;
301+
}
302+
303+
private static function getEnvVariable($name)
304+
{
305+
if (isset($_SERVER[$name])) {
306+
return (string) $_SERVER[$name];
307+
}
308+
309+
if (PHP_SAPI === 'cli' && ($value = getenv($name)) !== false && $value !== null) {
310+
return (string) $value;
311+
}
312+
313+
return false;
314+
}
315+
316+
private static function caFileUsable($certFile, LoggerInterface $logger = null)
317+
{
318+
return $certFile && @is_file($certFile) && @is_readable($certFile) && static::validateCaFile($certFile, $logger);
319+
}
320+
321+
private static function caDirUsable($certDir)
322+
{
323+
return $certDir && @is_dir($certDir) && @is_readable($certDir) && glob($certDir . '/*');
324+
}
325+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
namespace Doctrine\Bundle\DoctrineCacheBundle;
3+
4+
use Symfony\Component\Console\Application;
5+
use Symfony\Component\HttpKernel\Bundle\Bundle;
6+
7+
/**
8+
* Symfony Bundle for Doctrine Cache
9+
*
10+
* @author Guilherme Blanco <[email protected]>
11+
* @author Fabio B. Silva <[email protected]>
12+
*/
13+
class DoctrineCacheBundle extends Bundle
14+
{
15+
/**
16+
* {@inheritDoc}
17+
*/
18+
public function registerCommands(Application $application)
19+
{
20+
}
21+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
3+
use Symfony\Component\HttpKernel\Bundle\Bundle;

0 commit comments

Comments
 (0)