1
0
Fork 0

Fix Composer autoloader being hijackable by script/plugin event handlers (#11955)

pull/11963/head
Jordi Boggiano 2024-04-29 11:41:33 +02:00 committed by GitHub
parent d4396a85bf
commit bcab1c4b8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 50 additions and 22 deletions

View File

@ -180,11 +180,6 @@ parameters:
count: 2
path: ../src/Composer/Util/Hg.php
-
message: "#^Only booleans are allowed in &&, int\\<0, 2097152\\> given on the right side\\.$#"
count: 1
path: ../src/Composer/Util/Http/CurlDownloader.php
-
message: "#^Parameter \\#1 \\$multi_handle of function curl_multi_add_handle expects CurlMultiHandle, resource\\|null given\\.$#"
count: 1

View File

@ -332,7 +332,7 @@ parameters:
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 4
count: 3
path: ../src/Composer/Command/DiagnoseCommand.php
-
@ -1850,6 +1850,11 @@ parameters:
count: 1
path: ../src/Composer/Downloader/ZipDownloader.php
-
message: "#^Argument of an invalid type array\\<int, callable\\>\\|false supplied for foreach, only iterables are supported\\.$#"
count: 2
path: ../src/Composer/EventDispatcher/EventDispatcher.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
@ -4000,7 +4005,7 @@ parameters:
-
message: "#^Cannot access offset 'features' on array\\|false\\.$#"
count: 2
count: 1
path: ../src/Composer/Util/Http/CurlDownloader.php
-
@ -4010,11 +4015,6 @@ parameters:
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 3
path: ../src/Composer/Util/Http/CurlDownloader.php
-
message: "#^Only booleans are allowed in &&, int given on the right side\\.$#"
count: 1
path: ../src/Composer/Util/Http/CurlDownloader.php
@ -4188,11 +4188,6 @@ parameters:
count: 1
path: ../src/Composer/Util/Http/CurlDownloader.php
-
message: "#^Method Composer\\\\Util\\\\Http\\\\RequestProxy::getCurlOptions\\(\\) should return array\\<int, int\\|string\\> but returns array\\<int\\|string, int\\|string\\>.$#"
count: 1
path: ../src/Composer/Util/Http/RequestProxy.php
-
message: "#^Constant CURLOPT_PROXY_CAINFO not found\\.$#"
count: 1
@ -4203,6 +4198,11 @@ parameters:
count: 1
path: ../src/Composer/Util/Http/RequestProxy.php
-
message: "#^Method Composer\\\\Util\\\\Http\\\\RequestProxy\\:\\:getCurlOptions\\(\\) should return array\\<int, int\\|string\\> but returns array\\<int\\|string, int\\|string\\>\\.$#"
count: 1
path: ../src/Composer/Util/Http/RequestProxy.php
-
message: "#^Cannot call method abortRequest\\(\\) on Composer\\\\Util\\\\Http\\\\CurlDownloader\\|null\\.$#"
count: 1
@ -4618,11 +4618,6 @@ parameters:
count: 1
path: ../src/Composer/Util/StreamContextFactory.php
-
message: "#^Only booleans are allowed in an if condition, array given\\.$#"
count: 1
path: ../src/Composer/Util/StreamContextFactory.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2

View File

@ -194,6 +194,8 @@ class EventDispatcher
$this->pushEvent($event);
$autoloadersBefore = spl_autoload_functions();
try {
$returnMax = 0;
foreach ($listeners as $callable) {
@ -411,6 +413,26 @@ class EventDispatcher
}
} finally {
$this->popEvent();
$knownIdentifiers = [];
foreach ($autoloadersBefore as $key => $cb) {
$knownIdentifiers[$this->getCallbackIdentifier($cb)] = ['key' => $key, 'callback' => $cb];
}
foreach (spl_autoload_functions() as $cb) {
// once we get to the first known autoloader, we can leave any appended autoloader without problems
if (isset($knownIdentifiers[$this->getCallbackIdentifier($cb)]) && $knownIdentifiers[$this->getCallbackIdentifier($cb)]['key'] === 0) {
break;
}
// other newly appeared prepended autoloaders should be appended instead to ensure Composer loads its classes first
if ($cb instanceof ClassLoader) {
$cb->unregister();
$cb->register(false);
} else {
spl_autoload_unregister($cb);
spl_autoload_register($cb);
}
}
}
return $returnMax;
@ -638,4 +660,20 @@ class EventDispatcher
}
}
}
private function getCallbackIdentifier(callable $cb): string
{
if (is_string($cb)) {
return 'fn:'.$cb;
}
if (is_object($cb)) {
return 'obj:'.spl_object_hash($cb);
}
if (is_array($cb)) {
return 'array:'.(is_string($cb[0]) ? $cb[0] : get_class($cb[0]) .'#'.spl_object_hash($cb[0])).'::'.$cb[1];
}
// not great but also do not want to break everything here
return 'unsupported';
}
}