Skip to content

Commit

Permalink
Fix a bunch of type errors in SelfUpdateCommand, fixes #10696, closes #…
Browse files Browse the repository at this point in the history
  • Loading branch information
Seldaek committed Apr 6, 2022
1 parent b03e9e4 commit 106149d
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 31 deletions.
69 changes: 41 additions & 28 deletions src/Composer/Command/SelfUpdateCommand.php
Expand Up @@ -100,7 +100,7 @@ class_exists('Composer\Downloader\FilesystemException');

// switch channel if requested
$requestedChannel = null;
foreach (Versions::$channels as $channel) {
foreach (Versions::CHANNELS as $channel) {
if ($input->getOption($channel)) {
$requestedChannel = $channel;
$versionsUtil->setChannel($channel);
Expand All @@ -115,7 +115,10 @@ class_exists('Composer\Downloader\FilesystemException');
$cacheDir = $config->get('cache-dir');
$rollbackDir = $config->get('data-dir');
$home = $config->get('home');
$localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
$localFilename = realpath($_SERVER['argv'][0]);
if (false === $localFilename) {
$localFilename = $_SERVER['argv'][0];
}

if ($input->getOption('update-keys')) {
$this->fetchKeys($io, $config);
Expand All @@ -138,10 +141,13 @@ class_exists('Composer\Downloader\FilesystemException');

// check if composer is running as the same user that owns the directory root, only if POSIX is defined and callable
if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) {
$composeUser = posix_getpwuid(posix_geteuid());
$homeOwner = posix_getpwuid(fileowner($home));
if (isset($composeUser['name'], $homeOwner['name']) && $composeUser['name'] !== $homeOwner['name']) {
$io->writeError('<warning>You are running Composer as "'.$composeUser['name'].'", while "'.$home.'" is owned by "'.$homeOwner['name'].'"</warning>');
$composerUser = posix_getpwuid(posix_geteuid());
$homeDirOwnerId = fileowner($home);
if (is_array($composerUser) && $homeDirOwnerId !== false) {
$homeOwner = posix_getpwuid($homeDirOwnerId);
if (is_array($homeOwner) && isset($composerUser['name'], $homeOwner['name']) && $composerUser['name'] !== $homeOwner['name']) {
$io->writeError('<warning>You are running Composer as "'.$composerUser['name'].'", while "'.$home.'" is owned by "'.$homeOwner['name'].'"</warning>');
}
}
}

Expand All @@ -157,12 +163,12 @@ class_exists('Composer\Downloader\FilesystemException');
$latestPreview = $latestStable;
}
$latestVersion = $latest['version'];
$updateVersion = $input->getArgument('version') ?: $latestVersion;
$updateVersion = $input->getArgument('version') ?? $latestVersion;
$currentMajorVersion = Preg::replace('{^(\d+).*}', '$1', Composer::getVersion());
$updateMajorVersion = Preg::replace('{^(\d+).*}', '$1', $updateVersion);
$previewMajorVersion = Preg::replace('{^(\d+).*}', '$1', $latestPreview['version']);

if ($versionsUtil->getChannel() === 'stable' && !$input->getArgument('version')) {
if ($versionsUtil->getChannel() === 'stable' && null === $input->getArgument('version')) {
// if requesting stable channel and no specific version, avoid automatically upgrading to the next major
// simply output a warning that the next major stable is available and let users upgrade to it manually
if ($currentMajorVersion < $updateMajorVersion) {
Expand Down Expand Up @@ -243,7 +249,7 @@ class_exists('Composer\Downloader\FilesystemException');
$httpDownloader->copy($remoteFilename, $tempFilename);
$io->writeError('');

if (!file_exists($tempFilename) || !$signature) {
if (!file_exists($tempFilename) || null === $signature || '' === $signature) {
$io->writeError('<error>The download of the new composer version failed for an unexpected reason</error>');

return 1;
Expand Down Expand Up @@ -306,12 +312,15 @@ class_exists('Composer\Downloader\FilesystemException');
throw new \RuntimeException('Failed loading the public key from '.$sigFile);
}
$algo = defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'SHA384';
if (!in_array('sha384', array_map('strtolower', openssl_get_md_methods()))) {
if (!in_array('sha384', array_map('strtolower', openssl_get_md_methods()), true)) {
throw new \RuntimeException('SHA384 is not supported by your openssl extension, could not verify the phar file integrity');
}
$signature = json_decode($signature, true);
$signature = base64_decode($signature['sha384']);
$verified = 1 === openssl_verify(file_get_contents($tempFilename), $signature, $pubkeyid, $algo);
$signatureData = json_decode($signature, true);
$signatureSha384 = base64_decode($signatureData['sha384'], true);
if (false === $signatureSha384) {
throw new \RuntimeException('Failed loading the phar signature from '.$remoteFilename.'.sig, got '.$signature);
}
$verified = 1 === openssl_verify((string) file_get_contents($tempFilename), $signatureSha384, $pubkeyid, $algo);

// PHP 8 automatically frees the key instance and deprecates the function
if (PHP_VERSION_ID < 80000) {
Expand Down Expand Up @@ -405,7 +414,7 @@ protected function fetchKeys(IOInterface $io, Config $config): void
protected function rollback(OutputInterface $output, string $rollbackDir, string $localFilename): int
{
$rollbackVersion = $this->getLastBackupVersion($rollbackDir);
if (!$rollbackVersion) {
if (null === $rollbackVersion) {
throw new \UnexpectedValueException('Composer rollback failed: no installation to roll back to in "'.$rollbackDir.'"');
}

Expand Down Expand Up @@ -439,21 +448,24 @@ protected function rollback(OutputInterface $output, string $rollbackDir, string
protected function setLocalPhar(string $localFilename, string $newFilename, string $backupTarget = null): bool
{
$io = $this->getIO();
@chmod($newFilename, fileperms($localFilename));
$perms = @fileperms($localFilename);
if ($perms !== false) {
@chmod($newFilename, $perms);
}

// check phar validity
if (!$this->validatePhar($newFilename, $error)) {
$io->writeError('<error>The '.($backupTarget ? 'update' : 'backup').' file is corrupted ('.$error.')</error>');
$io->writeError('<error>The '.($backupTarget !== null ? 'update' : 'backup').' file is corrupted ('.$error.')</error>');

if ($backupTarget) {
if ($backupTarget !== null) {
$io->writeError('<error>Please re-run the self-update command to try again.</error>');
}

return false;
}

// copy current file into backups dir
if ($backupTarget) {
if ($backupTarget !== null) {
@copy($localFilename, $backupTarget);
}

Expand All @@ -477,7 +489,7 @@ protected function setLocalPhar(string $localFilename, string $newFilename, stri
}

@unlink($newFilename);
$action = 'Composer '.($backupTarget ? 'update' : 'rollback');
$action = 'Composer '.($backupTarget !== null ? 'update' : 'rollback');
throw new FilesystemException($action.' failed: "'.$localFilename.'" could not be written.'.PHP_EOL.$e->getMessage());
}
}
Expand All @@ -495,7 +507,7 @@ protected function cleanBackups(string $rollbackDir, ?string $except = null): vo
$fs = new Filesystem;

foreach ($finder as $file) {
if ($except && $file->getBasename(self::OLD_INSTALL_EXT) === $except) {
if ($file->getBasename(self::OLD_INSTALL_EXT) === $except) {
continue;
}
$file = (string) $file;
Expand All @@ -504,21 +516,17 @@ protected function cleanBackups(string $rollbackDir, ?string $except = null): vo
}
}

/**
* @param string $rollbackDir
* @return string|false
*/
protected function getLastBackupVersion(string $rollbackDir)
protected function getLastBackupVersion(string $rollbackDir): ?string
{
$finder = $this->getOldInstallationFinder($rollbackDir);
$finder->sortByName();
$files = iterator_to_array($finder);

if (count($files)) {
if (count($files) > 0) {
return end($files)->getBasename(self::OLD_INSTALL_EXT);
}

return false;
return null;
}

/**
Expand Down Expand Up @@ -548,7 +556,7 @@ protected function getOldInstallationFinder(string $rollbackDir): Finder
*/
protected function validatePhar(string $pharFile, ?string &$error): bool
{
if (ini_get('phar.readonly')) {
if ((bool) ini_get('phar.readonly')) {
return true;
}

Expand Down Expand Up @@ -610,6 +618,11 @@ protected function tryAsWindowsAdmin(string $localFilename, string $newFilename)
}

$tmpFile = tempnam(sys_get_temp_dir(), '');
if (false === $tmpFile) {
$io->writeError('<error>Operation failed.'.$helpMessage.'</error>');

return false;
}
$script = $tmpFile.'.vbs';
rename($tmpFile, $script);

Expand Down
2 changes: 1 addition & 1 deletion src/Composer/Console/Application.php
Expand Up @@ -548,7 +548,7 @@ protected function getDefaultCommands(): array
new Command\ReinstallCommand(),
));

if (strpos(__FILE__, 'phar:') === 0) {
if (strpos(__FILE__, 'phar:') === 0 || '1' === Platform::getEnv('COMPOSER_TESTS_ARE_RUNNING')) {
$commands[] = new Command\SelfUpdateCommand();
}

Expand Down
9 changes: 7 additions & 2 deletions src/Composer/SelfUpdate/Versions.php
Expand Up @@ -21,8 +21,13 @@
*/
class Versions
{
/** @var string[] */
public static $channels = array('stable', 'preview', 'snapshot', '1', '2', '2.2');
/**
* @var string[]
* @deprecated use Versions::CHANNELS
*/
public static $channels = self::CHANNELS;

public const CHANNELS = ['stable', 'preview', 'snapshot', '1', '2', '2.2'];

/** @var HttpDownloader */
private $httpDownloader;
Expand Down
2 changes: 2 additions & 0 deletions tests/console-application.php
Expand Up @@ -12,4 +12,6 @@

require __DIR__ . '/../vendor/autoload.php';

\Composer\Util\Platform::putEnv('COMPOSER_TESTS_ARE_RUNNING', '1');

return new \Composer\Console\Application();

0 comments on commit 106149d

Please sign in to comment.